handle block changes

This commit is contained in:
Bixilon 2021-02-04 14:36:42 +01:00
parent 6be2ca79f3
commit 2605a35f68
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
12 changed files with 104 additions and 32 deletions

View File

@ -17,7 +17,17 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
data class BlockPosition(val x: Int, val y: Int, val z: Int) {
fun getChunkLocation(): ChunkLocation {
return ChunkLocation(this.x / ProtocolDefinition.SECTION_WIDTH_X, this.z / ProtocolDefinition.SECTION_WIDTH_Z)
val chunkX = if (this.x >= 0) {
this.x / ProtocolDefinition.SECTION_WIDTH_X
} else {
((this.x + 1) / ProtocolDefinition.SECTION_WIDTH_X) - 1
}
val chunkY = if (this.z >= 0) {
this.z / ProtocolDefinition.SECTION_WIDTH_Z
} else {
((this.z + 1) / ProtocolDefinition.SECTION_WIDTH_Z) - 1
}
return ChunkLocation(chunkX, chunkY)
}
fun getInChunkLocation(): InChunkLocation {
@ -32,6 +42,10 @@ data class BlockPosition(val x: Int, val y: Int, val z: Int) {
return InChunkLocation(x, this.y, z)
}
fun getSectionHeight(): Int {
return y / ProtocolDefinition.SECTION_HEIGHT_Y
}
override fun toString(): String {
return "($x $y $z)"
}

View File

@ -87,4 +87,13 @@ public class Chunk {
public HashMap<Integer, ChunkSection> getSections() {
return this.sections;
}
public ChunkSection getSectionOrCreate(int sectionHeight) {
ChunkSection section = this.sections.get(sectionHeight);
if (section == null) {
section = new ChunkSection();
this.sections.put(sectionHeight, section);
}
return section;
}
}

View File

@ -20,6 +20,10 @@ data class InChunkLocation(val x: Int, val y: Int, val z: Int) {
return InChunkSectionLocation(x, y % ProtocolDefinition.SECTION_HEIGHT_Y, z)
}
fun getSectionHeight(): Int {
return y / ProtocolDefinition.SECTION_HEIGHT_Y
}
override fun toString(): String {
return "($x $y $z)"
}

View File

@ -106,6 +106,7 @@ object ChunkPreparer {
"minecraft:bedrock" -> 0
"minecraft:dirt" -> 1
"minecraft:stone" -> 2
"minecraft:glass" -> 3
else -> 2
}
}

View File

@ -8,9 +8,9 @@ import org.lwjgl.opengl.GL20;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.glDrawArrays;
import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import static org.lwjgl.opengl.GL30.*;
public class Mesh {
int vAO;
@ -44,4 +44,9 @@ public class Mesh {
glBindVertexArray(this.vAO);
glDrawArrays(GL_TRIANGLES, 0, this.trianglesCount);
}
public void unload() {
glDeleteVertexArrays(this.vAO);
glDeleteBuffers(this.vBO);
}
}

View File

@ -1,5 +1,6 @@
package de.bixilon.minosoft.gui.rendering
import de.bixilon.minosoft.data.world.ChunkLocation
import de.bixilon.minosoft.protocol.network.Connection
import org.lwjgl.*
import org.lwjgl.glfw.Callbacks
@ -10,6 +11,7 @@ import org.lwjgl.opengl.*
import org.lwjgl.opengl.GL11.glClear
import org.lwjgl.system.MemoryStack
import org.lwjgl.system.MemoryUtil
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue
class RenderWindow(private val connection: Connection) {
@ -24,9 +26,10 @@ class RenderWindow(private val connection: Connection) {
private var lastFrame = 0.0
lateinit var camera: Camera
val meshesToDraw = ConcurrentLinkedQueue<Mesh>()
val renderQueue = ConcurrentLinkedQueue<Runnable>()
val chunkSectionsToDraw = ConcurrentHashMap<ChunkLocation, ConcurrentHashMap<Int, Mesh>>()
fun init() {
// Setup an error callback. The default implementation
@ -108,11 +111,7 @@ class RenderWindow(private val connection: Connection) {
}
fun startLoop() {
// val world = World()
// world.setChunk(ChunkLocation(0, 0), DummyData.getDummyChunk())
// world.setChunk(ChunkLocation(1, 0), DummyData.getDummyChunk())
texture0 = TextureArray(arrayOf("/textures/bedrock.png", "/textures/dirt.png", "/textures/stone.png"))
texture0 = TextureArray(arrayOf("/textures/bedrock.png", "/textures/dirt.png", "/textures/stone.png", "/textures/glass.png"))
texture0.load()
shader = Shader("vertex.glsl", "fragment.glsl")
@ -124,13 +123,6 @@ class RenderWindow(private val connection: Connection) {
camera.calculateViewMatrix(shader)
// for ((chunkLocation, chunk) in world.allChunks) {
// for ((sectionHeight, section) in chunk.sections) {
// meshesToDraw.add(Mesh(ChunkPreparer.prepareChunk(world, chunkLocation, sectionHeight, section), chunkLocation, sectionHeight))
// }
// }
var framesLastSecond = 0
var lastCalcTime = glfwGetTime()
while (!glfwWindowShouldClose(windowId)) {
@ -147,8 +139,10 @@ class RenderWindow(private val connection: Connection) {
camera.calculateViewMatrix(shader)
for (mesh in meshesToDraw) {
mesh.draw(shader)
for ((_, map) in chunkSectionsToDraw) {
for ((_, mesh) in map) {
mesh.draw(shader)
}
}
glfwSwapBuffers(windowId) // swap the color buffers
@ -157,17 +151,20 @@ class RenderWindow(private val connection: Connection) {
// invoked during this call.
glfwPollEvents()
handleInput()
camera.handleInput(deltaTime)
if (glfwGetTime() - lastCalcTime >= 1.0) {
glfwSetWindowTitle(windowId, "FPS: $framesLastSecond")
lastCalcTime = glfwGetTime()
framesLastSecond = 0
}
framesLastSecond++
for (renderQueueElement in renderQueue) {
renderQueueElement.run()
renderQueue.remove(renderQueueElement)
}
camera.handleInput(deltaTime)
if (glfwGetTime() - lastCalcTime >= 0.5) {
glfwSetWindowTitle(windowId, "FPS: ${framesLastSecond * 2}")
lastCalcTime = glfwGetTime()
framesLastSecond = 0
}
framesLastSecond++
}
}

View File

@ -7,8 +7,10 @@ import de.bixilon.minosoft.data.world.ChunkSection
import de.bixilon.minosoft.gui.rendering.ChunkPreparer.prepareChunk
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.util.Util
import de.bixilon.minosoft.util.logging.Log
import glm_.vec3.Vec3
import org.lwjgl.Version
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
@ -18,7 +20,7 @@ class Renderer(private val connection: Connection) {
fun start() {
Thread({
println("Hello LWJGL " + Version.getVersion() + "!")
Log.info("Hello LWJGL " + Version.getVersion() + "!")
renderWindow.init()
renderWindow.startLoop()
renderWindow.exit()
@ -26,6 +28,7 @@ class Renderer(private val connection: Connection) {
}
fun prepareChunk(chunkLocation: ChunkLocation, chunk: Chunk) {
renderWindow.chunkSectionsToDraw[chunkLocation] = ConcurrentHashMap()
for ((sectionHeight, section) in chunk.sections) {
prepareChunkSection(chunkLocation, sectionHeight, section)
}
@ -34,8 +37,23 @@ class Renderer(private val connection: Connection) {
fun prepareChunkSection(chunkLocation: ChunkLocation, sectionHeight: Int, section: ChunkSection) {
executor.execute {
val data = prepareChunk(connection.player.world, chunkLocation, sectionHeight, section)
val sectionMap = renderWindow.chunkSectionsToDraw[chunkLocation]!!
renderWindow.renderQueue.add {
renderWindow.meshesToDraw.add(Mesh(data, chunkLocation, sectionHeight))
sectionMap[sectionHeight]?.unload()
sectionMap.remove(sectionHeight)
sectionMap[sectionHeight] = Mesh(data, chunkLocation, sectionHeight)
}
}
}
fun clearCache() {
renderWindow.renderQueue.add {
for ((location, map) in renderWindow.chunkSectionsToDraw) {
for ((sectionHeight, mesh) in map) {
mesh.unload()
map.remove(sectionHeight)
}
renderWindow.chunkSectionsToDraw.remove(location)
}
}
}
@ -43,7 +61,6 @@ class Renderer(private val connection: Connection) {
fun teleport(position: Location) {
renderWindow.renderQueue.add {
renderWindow.camera.setPosition(Vec3(position.x, position.y, position.z))
}
}

View File

@ -14,6 +14,7 @@
package de.bixilon.minosoft.protocol.network;
import de.bixilon.minosoft.Minosoft;
import de.bixilon.minosoft.config.StaticConfiguration;
import de.bixilon.minosoft.data.Player;
import de.bixilon.minosoft.data.VelocityHandler;
import de.bixilon.minosoft.data.commands.CommandRootNode;
@ -57,6 +58,7 @@ public class Connection {
private final Player player;
private final String hostname;
private final Recipes recipes = new Recipes();
private final Renderer renderer = new Renderer(this);
private LinkedList<ServerAddress> addresses;
private int desiredVersionNumber = -1;
private ServerAddress address;
@ -71,7 +73,6 @@ public class Connection {
private CommandRootNode commandRootNode;
private ConnectionPing connectionStatusPing;
private ServerListPongEvent pong;
private final Renderer renderer = new Renderer(this);
public Connection(int connectionId, String hostname, Player player) {
this.connectionId = connectionId;
@ -369,7 +370,9 @@ public class Connection {
case FAILED_NO_RETRY -> handlePingCallbacks(null);
case PLAY -> {
Minosoft.CONNECTIONS.put(getConnectionId(), this);
this.renderer.start();
if (!StaticConfiguration.HEADLESS_MODE) {
this.renderer.start();
}
if (CLI.getCurrentConnection() == null) {
CLI.setCurrentConnection(this);

View File

@ -17,6 +17,7 @@ import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.tweaker.VersionTweaker;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.data.world.Chunk;
import de.bixilon.minosoft.data.world.ChunkSection;
import de.bixilon.minosoft.modding.event.events.BlockChangeEvent;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
@ -51,14 +52,18 @@ public class PacketBlockChange extends ClientboundPacket {
}
connection.fireEvent(new BlockChangeEvent(connection, this));
int sectionHeight = getPosition().getSectionHeight();
ChunkSection section = chunk.getSectionOrCreate(sectionHeight);
// tweak
if (!connection.getVersion().isFlattened()) {
Block block = VersionTweaker.transformBlock(getBlock(), chunk, getPosition().getInChunkLocation());
chunk.setBlock(getPosition().getInChunkLocation(), block);
section.setBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), block);
} else {
chunk.setBlock(getPosition().getInChunkLocation(), getBlock());
section.setBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), getBlock());
}
connection.getRenderer().prepareChunkSection(getPosition().getChunkLocation(), sectionHeight, section);
}
@Override

View File

@ -84,6 +84,8 @@ public class PacketChunkBulk extends ClientboundPacket {
getChunks().forEach(((location, chunk) -> connection.fireEvent(new ChunkDataChangeEvent(connection, location, chunk))));
connection.getPlayer().getWorld().setChunks(getChunks());
getChunks().forEach(((location, chunk) -> connection.getRenderer().prepareChunk(location, chunk)));
}
@Override

View File

@ -17,6 +17,7 @@ import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.tweaker.VersionTweaker;
import de.bixilon.minosoft.data.world.Chunk;
import de.bixilon.minosoft.data.world.ChunkLocation;
import de.bixilon.minosoft.data.world.ChunkSection;
import de.bixilon.minosoft.data.world.InChunkLocation;
import de.bixilon.minosoft.modding.event.events.MultiBlockChangeEvent;
import de.bixilon.minosoft.protocol.network.Connection;
@ -25,6 +26,7 @@ import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.util.logging.Log;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.*;
@ -99,6 +101,17 @@ public class PacketMultiBlockChange extends ClientboundPacket {
chunk.setBlock(entry.getKey(), block);
}
}
HashSet<Integer> sectionHeights = new HashSet<>();
for (var entry : this.blocks.entrySet()) {
sectionHeights.add(entry.getKey().getSectionHeight());
}
for (var sectionHeight : sectionHeights) {
ChunkSection section = chunk.getSectionOrCreate(sectionHeight);
connection.getRenderer().prepareChunkSection(getLocation(), sectionHeight, section);
}
}
@Override

View File

@ -89,6 +89,8 @@ public class PacketRespawn extends ClientboundPacket {
connection.getPlayer().getWorld().setDimension(getDimension());
connection.getPlayer().setSpawnConfirmed(false);
connection.getPlayer().setGameMode(getGameMode());
connection.getRenderer().clearCache();
}
@Override