Kinniken Posted February 14, 2018 Posted February 14, 2018 Hi all, I have a GUI in my mod that displays a map of nearby chunks with their load status and whether or not they are "force loaded" by my mod. In 1.7, it worked fine, in 1.12, it occasionally crashes with this error: net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:835) (Sorry, I don't have the full stack trace on hand, I'll add it when I can) This error actually happens in the main thread, not my GUI code, and I've had it before when doing actions in other threads that are not thread-safe. The code I'm running in my GUI is this: private void drawChunkMap(final int i, final int j) { if (Mill.serverWorlds.isEmpty()) { return; } final int windowXstart = (width - getXSize()) / 2; final int windowYstart = (height - getYSize()) / 2; final World world = Mill.serverWorlds.get(0).world; final MillWorld mw = Mill.serverWorlds.get(0); GL11.glDisable(2896 /* GL_LIGHTING */); GL11.glDisable(2929 /* GL_DEPTH_TEST */); final int startX = (getXSize() - (chunkMapSizeInBlocks / 8)) / 2; final int startY = (getYSize() - (chunkMapSizeInBlocks / 8)) / 2; final int posXstart = (player.chunkCoordX * 16) - (chunkMapSizeInBlocks / 2); final int posZstart = (player.chunkCoordZ * 16) - (chunkMapSizeInBlocks / 2); final int mouseX = (((i - startX - windowXstart) / 2) * 16) + posXstart; final int mouseZ = (((j - startY - windowYstart) / 2) * 16) + posZstart; drawGradientRect(startX - 2, startY - 2, startX + (chunkMapSizeInBlocks / 8) + 2, startY + (chunkMapSizeInBlocks / 8) + 2, 0x20000000, 0x20000000); final ArrayList<String> labels = new ArrayList<String>(); for (int x = posXstart; x < (posXstart + chunkMapSizeInBlocks); x += 16) { for (int z = posZstart; z < (posZstart + chunkMapSizeInBlocks); z += 16) { int colour = 0; if (!world.isChunkGeneratedAt(x / 16, z / 16)) { colour = 0x40111111; } else { final Chunk chunk = world.getChunkProvider().provideChunk(x / 16, z / 16); if (chunk.isLoaded()) { colour = 0xc000ff00; } else { colour = 0xc0ff0000; } drawPixel(startX + ((x - posXstart) / 8), startY + ((z - posZstart) / 8), colour); if ((mouseX == x) && (mouseZ == z)) { labels.add(MLN.string("chunk.chunkcoords", "" + (x / 16) + "/" + (z / 16))); } } } } // copy to avoid ConcurrentModificationException final ArrayList<Building> buildings = new ArrayList<Building>(mw.allBuildings()); for (final Building b : buildings) { if (b.isTownhall && (b.winfo != null) && (b.villageType != null)) { for (int x = b.winfo.mapStartX; x < (b.winfo.mapStartX + b.winfo.length); x += 16) { for (int z = b.winfo.mapStartZ; z < (b.winfo.mapStartZ + b.winfo.width); z += 16) { if ((x >= posXstart) && (x <= (posXstart + chunkMapSizeInBlocks)) && (z >= posZstart) && (z <= (posZstart + chunkMapSizeInBlocks))) { int colour; if (b.villageType.lonebuilding) { colour = 0xf0990099; } else { colour = 0xf00000ff; } drawPixel(startX + ((x - posXstart) / 8) + 1, startY + ((z - posZstart) / 8) + 1, colour); if ((mouseX == x) && (mouseZ == z)) { labels.add(MLN.string("chunk.village", b.getVillageQualifiedName())); } } } } } } boolean labelForced = false; for (final ChunkPos cc : ForgeChunkManager.getPersistentChunksFor(world).keys()) { if (((cc.x * 16) >= posXstart) && ((cc.x * 16) <= (posXstart + chunkMapSizeInBlocks)) && ((cc.z * 16) >= posZstart) && ((cc.z * 16) <= (posZstart + chunkMapSizeInBlocks))) { drawPixel(startX + (((cc.x * 16) - posXstart) / 8), startY + (((cc.z * 16) - posZstart) / 8) + 1, 0xf0ffffff); if ((mouseX == (cc.x * 16)) && (mouseZ == (cc.z * 16)) && !labelForced) { labels.add(MLN.string("chunk.chunkforced")); labelForced = true; } } } if (!labels.isEmpty()) { int stringlength = 0; for (final String s : labels) { final int w = fontRenderer.getStringWidth(s); if (w > stringlength) { stringlength = w; } } drawGradientRect((i - 3 - windowXstart) + 10, j - 3 - windowYstart, ((i + stringlength + 3) - windowXstart) + 10, (j + (11 * labels.size())) - windowYstart, 0xc0000000, 0xc0000000); for (int si = 0; si < labels.size(); si++) { fontRenderer.drawString(labels.get(si), (i - windowXstart) + 10, (j - windowYstart) + (11 * si), 0x909090); } } GL11.glEnable(2896 /* GL_LIGHTING */); GL11.glEnable(2929 /* GL_DEPTH_TEST */); } Can anybody confirm that it is my calls to the chunk provider that are forbidden from a GUI? And if that's the case, is there a recommanded way of dealing with this? I could have the data be pre-calculated in the main thread, but it would be an ugly hack... Thanks Quote http://www.millenaire.org/img/dynamicsig.png[/img]
SatyPardus Posted February 14, 2018 Posted February 14, 2018 (edited) The full error would be good. But I am suspecting a dictionary change while you are reading the chunks. But I can only confirm with the whole error. Edit: I am stupid. You are not modifying anything. But yet, a full error would help Edited February 14, 2018 by SatyPardus Quote
Kinniken Posted February 15, 2018 Author Posted February 15, 2018 I realise I'm not supposed to do this, but as a debugging tool for SP-only usage it worked fine in 1.7. I'm just wondering if anybody can confirm that yes, you can no longer access chunks for other threads than the main one in 1.12. The Client-Server thing is not the real issue there, I assume I'd have the same problems if I was running that code in a thread split from the main server thread. After that I could of course fix it by using packets, it's just a lot more work for a very side feature. And those static references are because my mod requires a large amount of world-specific data that can't be accessed via Minecraft means like DimensionManager. Quote http://www.millenaire.org/img/dynamicsig.png[/img]
Kinniken Posted February 15, 2018 Author Posted February 15, 2018 It worked in 1.7. I've had thousands of players over the four years of the 1.7 version with this bug never reported, I saw it myself and had it reported to me several times for 1.12 in two days of having an alpha out. WorldSavedData is great but not suitable for saving the amount of data I need. It's not the perfect solution for everything. For one thing it requires saving everything to NBT, which is not cool for saving thousands of building points for planned constructions. Plus it does't allow you to finally control what gets sent to the client and what doesn't; in my case just sending everything would be a waste of bandwidth. Quote http://www.millenaire.org/img/dynamicsig.png[/img]
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.