-
-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ClientsideMap questions #8
Comments
Hello! You do not need a valid map id. The quickstart example just happens to use a valid id, but you can also use invalid ones. |
Thank you for your response, but I still have another question. Regarding the Maven tutorial provided in the WiKi, it seems that I am unable to successfully import maps into my project. It appears that there is no relevant repository available. Should I clone the project and compile it in order to use it? |
I encountered a problem when creating a map. I'm not sure why, but when I execute the following code, the original marker does not disappear. Is there no way to remove the original marker without relying solely on packets? package com.cocobeen.Commands;
import dev.cerus.maps.api.ClientsideMap;
import dev.cerus.maps.api.graphics.ClientsideMapGraphics;
import dev.cerus.maps.api.graphics.ColorCache;
import dev.cerus.maps.version.VersionAdapterFactory;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class TestCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) {
ClientsideMap map = new ClientsideMap(3);
ClientsideMapGraphics graphics = new ClientsideMapGraphics();
graphics.fillComplete(ColorCache.rgbToMap(255, 255, 255));
graphics.fillRect(5, 5, 118, 118, ColorCache.rgbToMap(255, 0, 0), 1f);
map.clearMarkers();
map.draw(graphics);
map.sendTo(new VersionAdapterFactory().makeAdapter(), true, (Player) commandSender);
return true;
}
} 2023-06-25.22-33-01.mp4 |
Furthermore, when using the following code to create an illegal MapID and following the instructions in the Wiki, it seems to have no effect. The map doesn't show any changes. package com.cocobeen.Commands;
import dev.cerus.maps.api.ClientsideMap;
import dev.cerus.maps.api.graphics.ClientsideMapGraphics;
import dev.cerus.maps.api.graphics.ColorCache;
import dev.cerus.maps.version.VersionAdapterFactory;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class TestCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) {
ClientsideMap map = new ClientsideMap();
ClientsideMapGraphics graphics = new ClientsideMapGraphics();
graphics.fillComplete(ColorCache.rgbToMap(255, 255, 255));
graphics.fillRect(5, 5, 118, 118, ColorCache.rgbToMap(255, 0, 0), 1f);
map.clearMarkers();
map.draw(graphics);
map.sendTo(new VersionAdapterFactory().makeAdapter(), true, (Player) commandSender);
return true;
}
} 2023-06-25.22-44-29.mp4 |
If you look closely you can see that the markers disappear, but the server updates real maps every tick, so they appear again one tick later.
The map item in your hand has a certain map id that it's displaying. You need to change the items map id to the id of the fake map. |
Looks like I forgot to deplay version 3.7.1, use 3.7.0 as a workaround |
So, regardless of whether the MapID is legal or illegal, is it necessary to provide a MapID to the ClientsideMap object in order to use it properly? Because I tried modifying the 'map' tag in the NBTTag of a map to a MapID that has not been created in Minecraft, and it only works properly when I provide that MapID to the ClientsideMap. However, if I don't define a MapID within the ClientsideMap, even if the map is invalid, it cannot be used properly. |
If you don't explicitly provide a map id when creating a ClientsideMap it will choose an id itself. maps/common/src/main/java/dev/cerus/maps/api/ClientsideMap.java Lines 26 to 28 in fff816b
You can get the id using |
Hey, are the issues you experienced solved? Do you have any further questions? |
I apologize for taking so long to get back to you. Most of the basic functionality issues have been resolved, but the only remaining problem is that Maven still doesn't have the new version 3.7.1. I need the plugin to work on version 1.20.1, so it would be great if you could update it for me. Thank you so much for your assistance. |
Sorry about that, should be fixed now |
I'm pretty sure that's caused by some sort of bug in your code, I've never had any issues with sending lots of data packets. If you're willing to share the relevant code I might be able to help you figure this out. |
package com.cocobeen;
import com.cocobeen.Commands.MapSaveCommand;
import com.cocobeen.Commands.MapTransferCommand;
import com.cocobeen.Listener.*;
import com.cocobeen.Utils.ImageData;
import com.cocobeen.Utils.MapGraphicsDrawUtils;
import com.cocobeen.Utils.SerializationUtils;
import com.cocobeen.Utils.struct.ChunkCache;
import com.cocobeen.Utils.struct.MapCache;
import de.tr7zw.nbtapi.NBTItem;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.GlowItemFrame;
import org.bukkit.entity.ItemFrame;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public final class CrossServerMap extends JavaPlugin {
private static CrossServerMap plugin = null;
private static MapDataDAO dataDAO = null;
private static byte[] banMapData = null;
private static ConcurrentLinkedDeque<MapCache> cache = new ConcurrentLinkedDeque<>();
private static ConcurrentLinkedDeque<Chunk> chunk_cache = new ConcurrentLinkedDeque<>();
private static ConcurrentHashMap<UUID, Set<Integer>> playerCache = new ConcurrentHashMap<>();
private static ConcurrentLinkedDeque<ChunkCache> draw_caches = new ConcurrentLinkedDeque<>();
@Override
public void onEnable() {
plugin = this;
getConfig().options().copyDefaults();
saveDefaultConfig();
initDatabase();
initCommands();
initListener();
MapGraphicsDrawTask();
DrawTask();
ClearCacheTask();
}
@Override
public void onDisable() {
getServer().getScheduler().cancelTasks(this);
banMapData = null;
cache.clear();
chunk_cache.clear();
playerCache.clear();
dataDAO.closeConnection();
}
public static CrossServerMap getPlugin() {
return plugin;
}
public static MapDataDAO getDataDAO() {
return dataDAO;
}
public static byte[] getBanMapData() {
return banMapData;
}
public static ConcurrentLinkedDeque<MapCache> getCache(){
return cache;
}
public static ConcurrentLinkedDeque<Chunk> getChunkCache(){
return chunk_cache;
}
public static ConcurrentHashMap<UUID, Set<Integer>> getPlayerCache() {
return playerCache;
}
public static void setCache(ConcurrentLinkedDeque<MapCache> MapCache){
cache = MapCache;
}
public static void setChunkCache(ConcurrentLinkedDeque<Chunk> chunkCache){
chunk_cache = chunkCache;
}
private void initListener(){
if (getServer().getPluginManager().getPlugin("MysqlPlayerDataBridge") != null){
getLogger().info("MysqlPlayerDataBridge Hook!");
getServer().getPluginManager().registerEvents(new MPDBSyncCompleteListener(), this);
}
else {
getServer().getPluginManager().registerEvents(new PlayerJoinListener(), this);
getLogger().info("MysqlPlayerDataBridge not found");
}
getServer().getPluginManager().registerEvents(new PlayerItemHeldListener(), this);
getServer().getPluginManager().registerEvents(new PlayerChunkLoadListener(), this);
getServer().getPluginManager().registerEvents(new PrepareItemCraftListener(), this);
getServer().getPluginManager().registerEvents(new InventoryClickListener(), this);
getServer().getPluginManager().registerEvents(new PlayerQuitListener(), this);
}
private void initCommands(){
getServer().getPluginCommand("mapsave").setExecutor(new MapSaveCommand());
getServer().getPluginCommand("maptransfer").setExecutor(new MapTransferCommand());
getServer().getPluginCommand("mapsave").setTabCompleter(new MapSaveCommand());
getServer().getPluginCommand("maptransfer").setTabCompleter(new MapTransferCommand());
}
private void initDatabase(){
final String user = getConfig().getString("mysql.user");
final String password = getConfig().getString("mysql.password");
final String host = getConfig().getString("mysql.host");
final int port = getConfig().getInt("mysql.port");
final String database = getConfig().getString("mysql.database");
Runnable task = () -> {
dataDAO = new MapDataDAO(host, port, database, user, password);
dataDAO.createTable();
ImageData data = new ImageData();
banMapData = data.getColors();
};
Bukkit.getScheduler().runTaskAsynchronously(this, task);
}
private void DrawTask(){
Runnable task = () -> {
synchronized (draw_caches){
if (draw_caches.size() == 0){
return;
}
List<ChunkCache> elements = draw_caches.stream()
.limit(5)
.collect(Collectors.toList());
for (ChunkCache chunkCache : elements){
byte[] color = chunkCache.getColor();
int mapId = chunkCache.getMapID();
draw_caches.remove(chunkCache);
MapGraphicsDrawUtils.MainThreadMapGraphicsDraw(color, mapId);
}
}
};
Bukkit.getScheduler().runTaskTimerAsynchronously(this, task, 3, 3);
}
private void MapGraphicsDrawTask(){
Runnable task = () -> {
ConcurrentLinkedDeque<Chunk> chunks = null;
synchronized (chunk_cache){
chunks = new ConcurrentLinkedDeque<>(chunk_cache);
chunk_cache.clear();
}
for (Chunk chunk : chunks){
Entity[] entities = chunk.getEntities();
Set<ItemFrame> itemFrames = new HashSet<>();
Set<GlowItemFrame> glowItemFrames = new HashSet<>();
for (Entity entity : entities){
EntityType type = entity.getType();
switch (type){
case ITEM_FRAME:{
itemFrames.add((ItemFrame) entity);
break;
}
case GLOW_ITEM_FRAME:{
glowItemFrames.add((GlowItemFrame) entity);
break;
}
}
}
for (ItemFrame itemFrame : itemFrames){
ItemStack itemStack = itemFrame.getItem();
if (itemStack.getType().equals(Material.FILLED_MAP)){
ChunkCache chunkCache = initMapGraphicsDraw(itemStack);
if (chunkCache != null){
synchronized (draw_caches){
draw_caches.add(chunkCache);
}
}
}
}
for (GlowItemFrame glowItemFrame : glowItemFrames){
ItemStack itemStack = glowItemFrame.getItem();
if (itemStack.getType().equals(Material.FILLED_MAP)){
ChunkCache chunkCache = initMapGraphicsDraw(itemStack);
if (chunkCache != null){
synchronized (draw_caches){
draw_caches.add(chunkCache);
}
}
}
}
}
};
Bukkit.getScheduler().runTaskTimerAsynchronously(this, task, 10, 10);
}
private ChunkCache initMapGraphicsDraw(ItemStack itemStack){
NBTItem nbtItem = new NBTItem(itemStack);
if (nbtItem.hasTag("CrossServerMap_Owner")){
UUID OwnerUUID = nbtItem.getUUID("CrossServerMap_Owner");
int mapID = nbtItem.getInteger("map");
synchronized (cache){
for (MapCache mapCache : cache){
if (mapCache.getMapID() == mapID){
byte[] color = mapCache.getColor();
return new ChunkCache(color, mapID);
}
}
boolean isBan = dataDAO.readMapBanState(mapID);
if (isBan){
MapGraphicsDrawUtils.MapGraphicsDraw(banMapData, mapID);
cache.addLast(new MapCache(OwnerUUID, banMapData, mapID));
return new ChunkCache(banMapData, mapID);
}
String data = dataDAO.readMapData(mapID);
if (data == null){
getLogger().warning("MapID " + mapID + " not found!");
getLogger().warning("This problem is very serious! Please check the database information " +
"and plugin settings immediately!");
return new ChunkCache(banMapData, mapID);
}
byte[] color = SerializationUtils.DeserializeMapData(data);
if (cache.size() > 1000){
plugin.getLogger().info("MapCaches is full! Remove caches head data");
cache.poll();
}
cache.addLast(new MapCache(OwnerUUID, color, mapID));
return new ChunkCache(color, mapID);
}
}
return null;
}
private void ClearCacheTask(){
Runnable task = () -> {
long start = System.nanoTime();
getLogger().info("Clear expired cache data...");
synchronized (cache){
ConcurrentLinkedDeque<MapCache> caches = new ConcurrentLinkedDeque<>(cache);
int count = 0;
LocalDateTime now = LocalDateTime.now();
for (MapCache mapCache : caches){
LocalDateTime time = mapCache.getTime();
Duration duration = Duration.between(time, now);
if (duration.compareTo(Duration.ofMinutes(10)) > 0){
count++;
cache.remove(mapCache);
}
}
long end = System.nanoTime();
long countTime = TimeUnit.MICROSECONDS.convert((end - start), TimeUnit.NANOSECONDS);
getLogger().info("There are " + cache.size() + " records in the current cache.");
getLogger().info("Successfully cleared " + count + " expired cache data and took " + countTime + " ms.");
}
};
Bukkit.getScheduler().runTaskTimerAsynchronously(this, task, 1200, 1200);
}
}
package com.cocobeen.Listener;
import com.cocobeen.CrossServerMap;
import io.papermc.paper.event.packet.PlayerChunkLoadEvent;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import java.util.concurrent.ConcurrentLinkedDeque;
public class PlayerChunkLoadListener implements Listener {
private final CrossServerMap plugin = CrossServerMap.getPlugin();
private final ConcurrentLinkedDeque<Chunk> chunks = CrossServerMap.getChunkCache();
@EventHandler
public void PlayerChunkLoad(PlayerChunkLoadEvent event){
Chunk chunk = event.getChunk();
if (Thread.holdsLock(chunks)){
Runnable task = () -> {
synchronized (chunk){
chunks.addLast(chunk);
}
};
Bukkit.getScheduler().runTaskAsynchronously(plugin, task);
return;
}
chunks.addLast(chunk);
}
}
package com.cocobeen.Utils;
import com.cocobeen.CrossServerMap;
import dev.cerus.maps.api.ClientsideMap;
import dev.cerus.maps.api.graphics.ClientsideMapGraphics;
import dev.cerus.maps.api.version.VersionAdapter;
import dev.cerus.maps.version.VersionAdapterFactory;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.Set;
import java.util.UUID;
public class MapGraphicsDrawUtils {
private static final VersionAdapter va = new VersionAdapterFactory().makeAdapter();
public static void MapGraphicsDraw(byte[] color, int id, Player player){
Runnable task = () -> {
ClientsideMap map = BaseMapGraphics(color, id);
map.sendTo(va, true, player);
};
Bukkit.getScheduler().runTaskAsynchronously(CrossServerMap.getPlugin(), task);
}
public static void MapGraphicsDraw(byte[] color, int id){
Runnable task = () -> {
ClientsideMap map = BaseMapGraphics(color, id);
for (Player player : Bukkit.getServer().getOnlinePlayers()) {
UUID uuid = player.getUniqueId();
if (CrossServerMap.getPlayerCache().containsKey(uuid)){
Set<Integer> mapList = CrossServerMap.getPlayerCache().get(uuid);
if (!mapList.contains(id)){
mapList.add(id);
CrossServerMap.getPlayerCache().put(uuid, mapList);
map.sendTo(va, true, player);
}
}
}
};
Bukkit.getScheduler().runTaskAsynchronously(CrossServerMap.getPlugin(), task);
}
public static void MainThreadMapGraphicsDraw(byte[] color, int id){
ClientsideMap map = BaseMapGraphics(color, id);
for (Player player : Bukkit.getOnlinePlayers()) {
UUID uuid = player.getUniqueId();
if (CrossServerMap.getPlayerCache().containsKey(uuid)){
Set<Integer> mapList = CrossServerMap.getPlayerCache().get(uuid);
if (!mapList.contains(id)){
mapList.add(id);
CrossServerMap.getPlayerCache().put(uuid, mapList);
map.sendTo(va, player);
}
}
}
}
private static ClientsideMap BaseMapGraphics(byte[] color, int id){
ClientsideMap map = new ClientsideMap(id);
ClientsideMapGraphics graphics = new ClientsideMapGraphics();
for (int x = 0; x != 128; x++){
for (int z = 0; z != 128; z++){
graphics.setPixel(x, z, color[x + z * 128]);
}
}
map.clearMarkers();
map.draw(graphics);
return map;
}
} |
The above is the code I'm using. I'm trying to scan item frames on the map when a player reads a block, retrieve the NBT tags of the items on them, and the map ID. After that, I send map data packets to all players. |
Are you 100% sure that your "color" arrays actually have colors in them? Looks like your sending a bunch of transparent maps to the player. |
Regarding this point, I am very certain that I have the correct color array because I also use other events to read the cache and draw individual maps, and there are no issues with them. |
To troubleshoot the issue, I also inserted |
To debug the issue further: In your private static ClientsideMap BaseMapGraphics(byte[] color, int id){
ClientsideMap map = new ClientsideMap(id);
ClientsideMapGraphics graphics = new ClientsideMapGraphics();
for (int x = 0; x != 128; x++){
for (int z = 0; z != 128; z++){
graphics.setPixel(x, z, color[x + z * 128]);
}
}
// Draw rectangle in top left corner
graphics.fillRect(0, 0, 8, 8, /* 18 = red */ (byte) 18, 1f);
map.clearMarkers();
map.draw(graphics);
return map;
} |
Hm, that's weird. Maybe the client is ignoring packets to prevent getting overloaded by the server? I have honestly no idea why this is happening. Maybe try sending fewer maps at the same time. |
I consider the probability of this to be relatively low because I have previously used a plugin called SyncStaticMapView, which also relied on map data packets, and it did not experience random display issues even under such high-density map display conditions. The problem arose when I migrated from that plugin and started using the maps library to display maps. The issue is severe, frequent, and occurs consistently in this scenario. https://www.spigotmc.org/resources/syncstaticmapview-archive.96333/ |
One last idea: Change |
Very weird. Sorry, no idea what's happening. |
I have temporarily resolved the issue by repeatedly sending map data to the players. I will continue to update this location if any further issues are discovered. |
Feature description
I found that in the Quickstart example, it is necessary to have a valid MapID in order to create a ClientsideMap. Is it possible to create a ClientsideMap without using a valid MapID? I don't want to require players to first create any files in the "./world/data" directory using the original method. Can alternative methods, such as custom NBTTag or other approaches, be provided to determine the ClientsideMap?
Relevant issues
No response
The text was updated successfully, but these errors were encountered: