11package com.lambda.client.module.modules.misc
22
3+ import com.lambda.client.LambdaMod
34import com.lambda.client.event.SafeClientEvent
45import com.lambda.client.module.Category
56import com.lambda.client.module.Module
7+ import com.lambda.client.util.FolderUtils
68import com.lambda.client.util.TickTimer
79import com.lambda.client.util.TimeUnit
10+ import com.lambda.client.util.items.inventorySlots
811import com.lambda.client.util.text.MessageSendHelper
912import com.lambda.client.util.threads.defaultScope
1013import com.lambda.client.util.threads.runSafe
@@ -25,59 +28,73 @@ import java.security.MessageDigest
2528import javax.imageio.ImageIO
2629
2730internal object MapDownloader : Module(
28- name = " Map Downloader " ,
31+ name = " MapDownloader " ,
2932 category = Category .MISC ,
3033 description = " Downloads maps in item frames in your render distance to file."
3134) {
3235
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 )
3541 private val pendingHashes = mutableSetOf<String >()
3642 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 >()
3944 private val secTimer = TickTimer (TimeUnit .SECONDS )
4045 private val milliSecTimer = TickTimer (TimeUnit .MILLISECONDS )
4146
4247 init {
43- val directory = File (mapPath)
44- if (! directory.exists()) {
45- directory.mkdir()
46- }
47-
4848 existingHashes = getExistingHashes()
4949
5050 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} " )
6068 }
61- pendingTasks.remove(triple)
6269 }
70+ pendingTasks.remove(mapInfo)
6371 }
64-
65- getMaps()
6672 }
73+
74+ getMaps()
75+ }
76+
77+ openImageFolder.consumers.add { _, it ->
78+ if (it) FolderUtils .openFolder(FolderUtils .mapImagesFolder)
79+ false
6780 }
6881 }
6982
7083 private fun getExistingHashes (): MutableSet <String > {
7184 val alreadyConverted = mutableSetOf<String >()
7285
73- File (mapPath ).walk().filter {
86+ File (FolderUtils .mapImagesFolder ).walk().filter {
7487 it.name.endsWith(" .png" )
7588 }.forEach { file ->
7689 val nameArr = file.name.split(" _" )
7790 if (nameArr.isNotEmpty()) {
7891 alreadyConverted.add(nameArr[0 ])
7992 }
8093 }
94+
95+ // to exclude the empty map
96+ alreadyConverted.add(" ce338fe6899778aacfc28414f2d9498b" )
97+
8198 return alreadyConverted
8299 }
83100
@@ -91,27 +108,39 @@ internal object MapDownloader : Module(
91108 }
92109
93110 private fun SafeClientEvent.getMaps () {
94- world.loadedEntityList
111+ if (saveMapsFromEntity) world.loadedEntityList
95112 .filterIsInstance<EntityItemFrame >()
96113 .filter { it.displayedItem.item == Items .FILLED_MAP }
97114 .forEach {
98115 (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+ }
101119
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)
110124 }
111125 }
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+ }
112141 }
113142
114- private fun SafeClientEvent.renderAndSaveMapImage (mapData : MapData , hash : String , mapID : Int ) {
143+ private fun SafeClientEvent.renderAndSaveMapImage (mapInfo : MapInfo ) {
115144 val finalSize = 128 * scale
116145
117146 var countPos = 0
@@ -122,15 +151,17 @@ internal object MapDownloader : Module(
122151
123152 repeat(128 ) { i ->
124153 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+ }
126157 countPos++
127158 }
128159 }
129160
130161 try {
131162 val resized = BufferedImage (finalSize, finalSize, img.type)
132163 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"
134165 g.setRenderingHint(RenderingHints .KEY_INTERPOLATION ,
135166 RenderingHints .VALUE_INTERPOLATION_NEAREST_NEIGHBOR )
136167 g.drawImage(img, 0 , 0 , finalSize, finalSize, 0 , 0 , img.width,
@@ -141,9 +172,11 @@ internal object MapDownloader : Module(
141172 ex.printStackTrace()
142173 }
143174
144- pendingHashes.remove(hash)
145- existingHashes.add(hash)
175+ pendingHashes.remove(mapInfo. hash)
176+ existingHashes.add(mapInfo. hash)
146177 }
147178
148179 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 )
149182}
0 commit comments