1
1
package com.lambda.client.module.modules.misc
2
2
3
+ import com.lambda.client.LambdaMod
3
4
import com.lambda.client.event.SafeClientEvent
4
5
import com.lambda.client.module.Category
5
6
import com.lambda.client.module.Module
7
+ import com.lambda.client.util.FolderUtils
6
8
import com.lambda.client.util.TickTimer
7
9
import com.lambda.client.util.TimeUnit
10
+ import com.lambda.client.util.items.inventorySlots
8
11
import com.lambda.client.util.text.MessageSendHelper
9
12
import com.lambda.client.util.threads.defaultScope
10
13
import com.lambda.client.util.threads.runSafe
@@ -25,59 +28,73 @@ import java.security.MessageDigest
25
28
import javax.imageio.ImageIO
26
29
27
30
internal object MapDownloader : Module(
28
- name = " Map Downloader " ,
31
+ name = " MapDownloader " ,
29
32
category = Category .MISC ,
30
33
description = " Downloads maps in item frames in your render distance to file."
31
34
) {
32
35
33
- private var scale by setting(" Scale" , 1 , 1 .. 20 , 1 )
34
- private var saveDelay by setting(" Save delay" , 0.2 , 0.1 .. 2.0 , 0.1 )
36
+ private val scale by setting(" Scale" , 1 , 1 .. 20 , 1 , description = " Higher scale results in higher storage use!" )
37
+ private val saveMapsFromEntity by setting(" Save maps from entity" , true )
38
+ private val saveMapsFromInventory by setting(" Save maps from inventory" , true , description = " When rendering a new map it will save one image for every map update!" )
39
+ private val saveDelay by setting(" Save delay" , 0.2 , 0.1 .. 2.0 , 0.1 , unit = " s" )
40
+ private val openImageFolder = setting(" Open Image Folder..." , false )
35
41
private val pendingHashes = mutableSetOf<String >()
36
42
private var existingHashes = mutableSetOf<String >()
37
- private var pendingTasks = mutableSetOf<Triple <MapData , String , Int >>()
38
- private val mapPath = " mapImages${File .separator} "
43
+ private var pendingTasks = mutableSetOf<MapInfo >()
39
44
private val secTimer = TickTimer (TimeUnit .SECONDS )
40
45
private val milliSecTimer = TickTimer (TimeUnit .MILLISECONDS )
41
46
42
47
init {
43
- val directory = File (mapPath)
44
- if (! directory.exists()) {
45
- directory.mkdir()
46
- }
47
-
48
48
existingHashes = getExistingHashes()
49
49
50
50
safeListener<TickEvent .ClientTickEvent > {
51
- if (it.phase == TickEvent .Phase .START ) {
52
- if (secTimer.tick(10 )) existingHashes = getExistingHashes()
53
- if (pendingTasks.isNotEmpty()
54
- && milliSecTimer.tick((saveDelay * 1000 ).toInt())) {
55
- pendingTasks.firstOrNull()?.let { triple ->
56
- defaultScope.launch {
57
- runSafe {
58
- renderAndSaveMapImage(triple.first, triple.second, triple.third)
59
- }
51
+ if (it.phase != TickEvent .Phase .START ) return @safeListener
52
+
53
+ if (secTimer.tick(10 )) existingHashes = getExistingHashes()
54
+
55
+ if (pendingTasks.isNotEmpty()
56
+ && milliSecTimer.tick((saveDelay * 1000 ).toInt())
57
+ ) {
58
+ val directory = File (FolderUtils .mapImagesFolder)
59
+ if (! directory.exists()) {
60
+ directory.mkdir()
61
+ }
62
+
63
+ pendingTasks.firstOrNull()?.let { mapInfo ->
64
+ defaultScope.launch {
65
+ runSafe {
66
+ renderAndSaveMapImage(mapInfo)
67
+ LambdaMod .LOG .info(" Saved map - name: ${mapInfo.name} id: ${mapInfo.id} " )
60
68
}
61
- pendingTasks.remove(triple)
62
69
}
70
+ pendingTasks.remove(mapInfo)
63
71
}
64
-
65
- getMaps()
66
72
}
73
+
74
+ getMaps()
75
+ }
76
+
77
+ openImageFolder.consumers.add { _, it ->
78
+ if (it) FolderUtils .openFolder(FolderUtils .mapImagesFolder)
79
+ false
67
80
}
68
81
}
69
82
70
83
private fun getExistingHashes (): MutableSet <String > {
71
84
val alreadyConverted = mutableSetOf<String >()
72
85
73
- File (mapPath ).walk().filter {
86
+ File (FolderUtils .mapImagesFolder ).walk().filter {
74
87
it.name.endsWith(" .png" )
75
88
}.forEach { file ->
76
89
val nameArr = file.name.split(" _" )
77
90
if (nameArr.isNotEmpty()) {
78
91
alreadyConverted.add(nameArr[0 ])
79
92
}
80
93
}
94
+
95
+ // to exclude the empty map
96
+ alreadyConverted.add(" ce338fe6899778aacfc28414f2d9498b" )
97
+
81
98
return alreadyConverted
82
99
}
83
100
@@ -91,27 +108,39 @@ internal object MapDownloader : Module(
91
108
}
92
109
93
110
private fun SafeClientEvent.getMaps () {
94
- world.loadedEntityList
111
+ if (saveMapsFromEntity) world.loadedEntityList
95
112
.filterIsInstance<EntityItemFrame >()
96
113
.filter { it.displayedItem.item == Items .FILLED_MAP }
97
114
.forEach {
98
115
(it.displayedItem.item as ItemMap ).getMapData(it.displayedItem, world)?.let { mapData ->
99
- MessageDigest .getInstance(" MD5" )?.let { md ->
100
- val hash = md.digest(mapData.colors).toHex()
116
+ handleMap(mapData, it.displayedItem.displayName, it.displayedItem.itemDamage)
117
+ }
118
+ }
101
119
102
- if (! existingHashes.contains(hash) && ! pendingHashes.contains(hash)) {
103
- pendingHashes.add(hash)
104
- pendingTasks.add(Triple (mapData, hash, it.displayedItem.itemDamage))
105
- }
106
- } ? : run {
107
- MessageSendHelper .sendChatMessage(" $chatName Can't find MD5 instance." )
108
- disable()
109
- }
120
+ if (saveMapsFromInventory) player.inventorySlots.forEach {
121
+ if (it.stack.item is ItemMap ) {
122
+ (it.stack.item as ItemMap ).getMapData(it.stack, world)?.let { mapData ->
123
+ handleMap(mapData, it.stack.displayName, it.stack.itemDamage)
110
124
}
111
125
}
126
+ }
127
+ }
128
+
129
+ private fun handleMap (data : MapData , name : String , id : Int ) {
130
+ MessageDigest .getInstance(" MD5" )?.let { md ->
131
+ val hash = md.digest(data.colors).toHex()
132
+
133
+ if (! existingHashes.contains(hash) && ! pendingHashes.contains(hash)) {
134
+ pendingHashes.add(hash)
135
+ pendingTasks.add(MapInfo (data, name, id, hash))
136
+ }
137
+ } ? : run {
138
+ MessageSendHelper .sendChatMessage(" $chatName Can't find MD5 instance." )
139
+ disable()
140
+ }
112
141
}
113
142
114
- private fun SafeClientEvent.renderAndSaveMapImage (mapData : MapData , hash : String , mapID : Int ) {
143
+ private fun SafeClientEvent.renderAndSaveMapImage (mapInfo : MapInfo ) {
115
144
val finalSize = 128 * scale
116
145
117
146
var countPos = 0
@@ -122,15 +151,17 @@ internal object MapDownloader : Module(
122
151
123
152
repeat(128 ) { i ->
124
153
repeat(128 ) { j ->
125
- mapData.colors[countPos].toUByte().let { mapColor(it).let { it1 -> img.setRGB(j, i, it1.rgb) } }
154
+ mapInfo.data.colors[countPos].toUByte().let {
155
+ mapColor(it).let { color -> img.setRGB(j, i, color.rgb) }
156
+ }
126
157
countPos++
127
158
}
128
159
}
129
160
130
161
try {
131
162
val resized = BufferedImage (finalSize, finalSize, img.type)
132
163
val g = resized.createGraphics()
133
- val loc = " ${mapPath }${hash} _id_${mapID} _server_ ${ player.connection.networkManager.remoteAddress.toString().split( ' / ' )[ 0 ] ? : " local " } .png"
164
+ val loc = " ${FolderUtils .mapImagesFolder }${mapInfo. hash} _name_ ${mapInfo.name.replace( File .separator, " " )} _id_${mapInfo.id} _ ${ if (mc.isIntegratedServerRunning) " local " else " server_ ${ player.connection.networkManager.remoteAddress.toString().replace( " / " , " _ " ).replace( " : " , " _ " )} " } .png"
134
165
g.setRenderingHint(RenderingHints .KEY_INTERPOLATION ,
135
166
RenderingHints .VALUE_INTERPOLATION_NEAREST_NEIGHBOR )
136
167
g.drawImage(img, 0 , 0 , finalSize, finalSize, 0 , 0 , img.width,
@@ -141,9 +172,11 @@ internal object MapDownloader : Module(
141
172
ex.printStackTrace()
142
173
}
143
174
144
- pendingHashes.remove(hash)
145
- existingHashes.add(hash)
175
+ pendingHashes.remove(mapInfo. hash)
176
+ existingHashes.add(mapInfo. hash)
146
177
}
147
178
148
179
private fun ByteArray.toHex (): String = joinToString(separator = " " ) { eachByte -> " %02x" .format(eachByte) }
180
+
181
+ data class MapInfo (val data : MapData , val name : String , val id : Int , val hash : String )
149
182
}
0 commit comments