mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-06 00:17:58 +01:00
Merge remote-tracking branch 'origin/1.16.2' into 1.16.2
This commit is contained in:
commit
84d1f1bdaa
@ -112,7 +112,7 @@ dependencies {
|
|||||||
annotationProcessor 'org.projectlombok:lombok:1.18.12'
|
annotationProcessor 'org.projectlombok:lombok:1.18.12'
|
||||||
|
|
||||||
// Path finding
|
// Path finding
|
||||||
api 'com.github.MadMartian:hydrazine-path-finding:1.3.0'
|
api 'com.github.MadMartian:hydrazine-path-finding:1.3.1'
|
||||||
|
|
||||||
api "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
api "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
api 'com.github.jglrxavpok:Hephaistos:v1.0.5'
|
api 'com.github.jglrxavpok:Hephaistos:v1.0.5'
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit f81048bc208feab0db9bbb759debb7e7fe426b0c
|
Subproject commit 5466efe528108c7228ebe737ad5ac70af243e2e7
|
@ -16,6 +16,7 @@ import net.minestom.server.map.MapColors;
|
|||||||
import net.minestom.server.map.framebuffers.LargeDirectFramebuffer;
|
import net.minestom.server.map.framebuffers.LargeDirectFramebuffer;
|
||||||
import net.minestom.server.map.framebuffers.LargeGLFWFramebuffer;
|
import net.minestom.server.map.framebuffers.LargeGLFWFramebuffer;
|
||||||
import net.minestom.server.map.framebuffers.LargeGraphics2DFramebuffer;
|
import net.minestom.server.map.framebuffers.LargeGraphics2DFramebuffer;
|
||||||
|
import net.minestom.server.map.framebuffers.MapColorRenderer;
|
||||||
import net.minestom.server.network.packet.server.play.MapDataPacket;
|
import net.minestom.server.network.packet.server.play.MapDataPacket;
|
||||||
import net.minestom.server.utils.Position;
|
import net.minestom.server.utils.Position;
|
||||||
import net.minestom.server.utils.time.TimeUnit;
|
import net.minestom.server.utils.time.TimeUnit;
|
||||||
@ -43,17 +44,16 @@ public class Demo {
|
|||||||
LargeGraphics2DFramebuffer graphics2DFramebuffer = new LargeGraphics2DFramebuffer(512, 512);
|
LargeGraphics2DFramebuffer graphics2DFramebuffer = new LargeGraphics2DFramebuffer(512, 512);
|
||||||
LargeGLFWFramebuffer glfwFramebuffer = new LargeGLFWFramebuffer(512, 512);
|
LargeGLFWFramebuffer glfwFramebuffer = new LargeGLFWFramebuffer(512, 512);
|
||||||
|
|
||||||
glfwFramebuffer.useMapColors();
|
|
||||||
|
|
||||||
glfwFramebuffer.changeRenderingThreadToCurrent();
|
glfwFramebuffer.changeRenderingThreadToCurrent();
|
||||||
OpenGLRendering.init();
|
OpenGLRendering.init();
|
||||||
|
MapColorRenderer renderer = new MapColorRenderer(glfwFramebuffer, OpenGLRendering::render);
|
||||||
glfwFramebuffer.unbindContextFromThread();
|
glfwFramebuffer.unbindContextFromThread();
|
||||||
|
|
||||||
// renderingLoop(0, directFramebuffer, Demo::directRendering);
|
// renderingLoop(0, directFramebuffer, Demo::directRendering);
|
||||||
// renderingLoop(101, graphics2DFramebuffer, Demo::graphics2DRendering);
|
// renderingLoop(101, graphics2DFramebuffer, Demo::graphics2DRendering);
|
||||||
renderingLoop(201, glfwFramebuffer, f -> {});
|
renderingLoop(201, glfwFramebuffer, f -> {});
|
||||||
|
|
||||||
glfwFramebuffer.setupRenderLoop(15, TimeUnit.MILLISECOND, () -> OpenGLRendering.render(glfwFramebuffer));
|
glfwFramebuffer.setupRenderLoop(15, TimeUnit.MILLISECOND, renderer);
|
||||||
|
|
||||||
for (int x = -2; x <= 2; x++) {
|
for (int x = -2; x <= 2; x++) {
|
||||||
for (int z = -2; z <= 2; z++) {
|
for (int z = -2; z <= 2; z++) {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package net.minestom.demo.largeframebuffers;
|
package net.minestom.demo.largeframebuffers;
|
||||||
|
|
||||||
import net.minestom.server.map.MapColors;
|
|
||||||
import net.minestom.server.map.framebuffers.LargeGLFWFramebuffer;
|
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.lwjgl.BufferUtils;
|
import org.lwjgl.BufferUtils;
|
||||||
import org.lwjgl.opengl.GLUtil;
|
import org.lwjgl.opengl.GLUtil;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.*;
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -64,13 +64,11 @@ public final class OpenGLRendering {
|
|||||||
private static int projectionUniform;
|
private static int projectionUniform;
|
||||||
private static int viewUniform;
|
private static int viewUniform;
|
||||||
private static int modelUniform;
|
private static int modelUniform;
|
||||||
private static int paletteTexture;
|
|
||||||
private static int boxTexture;
|
private static int boxTexture;
|
||||||
|
|
||||||
static void init() {
|
static void init() {
|
||||||
GLUtil.setupDebugMessageCallback();
|
GLUtil.setupDebugMessageCallback();
|
||||||
|
|
||||||
paletteTexture = loadTexture("palette");
|
|
||||||
boxTexture = loadTexture("box");
|
boxTexture = loadTexture("box");
|
||||||
|
|
||||||
vbo = glGenBuffers();
|
vbo = glGenBuffers();
|
||||||
@ -81,15 +79,6 @@ public final class OpenGLRendering {
|
|||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW);
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(0);
|
|
||||||
glVertexAttribPointer(0, 3, GL_FLOAT, false, VERTEX_SIZE, 0); // position
|
|
||||||
glEnableVertexAttribArray(1);
|
|
||||||
glVertexAttribPointer(1, 2, GL_FLOAT, false, VERTEX_SIZE, 3*4); // color
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
||||||
|
|
||||||
// prepare matrices and shader
|
// prepare matrices and shader
|
||||||
renderShader = glCreateProgram();
|
renderShader = glCreateProgram();
|
||||||
projectionMatrix = new Matrix4f().setPerspective((float) (Math.PI/4f), 1f, 0.001f, 100f);
|
projectionMatrix = new Matrix4f().setPerspective((float) (Math.PI/4f), 1f, 0.001f, 100f);
|
||||||
@ -107,8 +96,6 @@ public final class OpenGLRendering {
|
|||||||
projectionUniform = glGetUniformLocation(renderShader, "projection");
|
projectionUniform = glGetUniformLocation(renderShader, "projection");
|
||||||
viewUniform = glGetUniformLocation(renderShader, "view");
|
viewUniform = glGetUniformLocation(renderShader, "view");
|
||||||
modelUniform = glGetUniformLocation(renderShader, "model");
|
modelUniform = glGetUniformLocation(renderShader, "model");
|
||||||
int paletteSizeUniform = glGetUniformLocation(renderShader, "paletteSize");
|
|
||||||
int paletteUniform = glGetUniformLocation(renderShader, "palette");
|
|
||||||
int boxUniform = glGetUniformLocation(renderShader, "box");
|
int boxUniform = glGetUniformLocation(renderShader, "box");
|
||||||
|
|
||||||
glUseProgram(renderShader); {
|
glUseProgram(renderShader); {
|
||||||
@ -116,9 +103,8 @@ public final class OpenGLRendering {
|
|||||||
uploadMatrix(viewUniform, viewMatrix);
|
uploadMatrix(viewUniform, viewMatrix);
|
||||||
|
|
||||||
glUniform1i(boxUniform, 0); // texture unit 0
|
glUniform1i(boxUniform, 0); // texture unit 0
|
||||||
glUniform1i(paletteUniform, 1); // texture unit 1
|
|
||||||
glUniform1f(paletteSizeUniform, 236);
|
|
||||||
}
|
}
|
||||||
|
glUseProgram(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int loadTexture(String filename) {
|
private static int loadTexture(String filename) {
|
||||||
@ -166,7 +152,6 @@ public final class OpenGLRendering {
|
|||||||
private static int createShader(String filename, int type) {
|
private static int createShader(String filename, int type) {
|
||||||
int shader = glCreateShader(type);
|
int shader = glCreateShader(type);
|
||||||
try(BufferedReader reader = new BufferedReader(new InputStreamReader(OpenGLRendering.class.getResourceAsStream(filename)))) {
|
try(BufferedReader reader = new BufferedReader(new InputStreamReader(OpenGLRendering.class.getResourceAsStream(filename)))) {
|
||||||
StringBuffer buffer = new StringBuffer();
|
|
||||||
String source = reader.lines().collect(Collectors.joining("\n"));
|
String source = reader.lines().collect(Collectors.joining("\n"));
|
||||||
glShaderSource(shader, source);
|
glShaderSource(shader, source);
|
||||||
glCompileShader(shader);
|
glCompileShader(shader);
|
||||||
@ -180,7 +165,7 @@ public final class OpenGLRendering {
|
|||||||
|
|
||||||
private static int frame = 0;
|
private static int frame = 0;
|
||||||
|
|
||||||
static void render(LargeGLFWFramebuffer framebuffer) {
|
static void render() {
|
||||||
if(frame % 100 == 0) {
|
if(frame % 100 == 0) {
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
long dt = time-lastTime;
|
long dt = time-lastTime;
|
||||||
@ -192,23 +177,24 @@ public final class OpenGLRendering {
|
|||||||
frame++;
|
frame++;
|
||||||
|
|
||||||
|
|
||||||
glClearColor(MapColors.COLOR_BLACK.multiply53()/255.0f, 0f, 0f, 1f);
|
glClearColor(0f, 0f, 0f, 1f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
modelMatrix.rotateY((float) (Math.PI/60f));
|
modelMatrix.rotateY((float) (Math.PI/60f));
|
||||||
|
|
||||||
// TODO: render texture
|
|
||||||
glUseProgram(renderShader); {
|
glUseProgram(renderShader); {
|
||||||
glActiveTexture(GL_TEXTURE1);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, paletteTexture);
|
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, boxTexture);
|
glBindTexture(GL_TEXTURE_2D, boxTexture);
|
||||||
|
|
||||||
uploadMatrix(modelUniform, modelMatrix);
|
uploadMatrix(modelUniform, modelMatrix);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, false, VERTEX_SIZE, 0); // position
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(1, 2, GL_FLOAT, false, VERTEX_SIZE, 3*4); // color
|
||||||
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||||
glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_INT, 0);
|
glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_INT, 0);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
package net.minestom.demo.largeframebuffers;
|
package net.minestom.server.map;
|
||||||
|
|
||||||
import net.minestom.server.map.MapColors;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
@ -17,7 +17,7 @@ import static org.lwjgl.glfw.GLFW.*;
|
|||||||
import static org.lwjgl.glfw.GLFW.glfwTerminate;
|
import static org.lwjgl.glfw.GLFW.glfwTerminate;
|
||||||
import static org.lwjgl.opengl.GL11.*;
|
import static org.lwjgl.opengl.GL11.*;
|
||||||
|
|
||||||
abstract class GLFWCapableBuffer {
|
public abstract class GLFWCapableBuffer {
|
||||||
|
|
||||||
protected final byte[] colors;
|
protected final byte[] colors;
|
||||||
private final ByteBuffer pixels;
|
private final ByteBuffer pixels;
|
||||||
|
@ -0,0 +1,257 @@
|
|||||||
|
package net.minestom.server.map.framebuffers;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL30.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class designed to help OpenGL users to convert their RGB values to map colors inside a post processing pass
|
||||||
|
* with a shader provided by Minestom.
|
||||||
|
*
|
||||||
|
* When rendering to a {@link GLFWFramebuffer} or a {@link LargeGLFWFramebuffer}, wrap your rendering in a MapColorRenderer to render to the GLFW with map colors.
|
||||||
|
*
|
||||||
|
* {@link MapColorRenderer} sets up an OpenGL framebuffer with the size of the underlying framebuffer and renders to it.
|
||||||
|
* The initialization of the framebuffer is done in the constructor.
|
||||||
|
* Therefore, the constructor call should be done inside the thread linked to the OpenGL context. The context can
|
||||||
|
* be moved through {@link GLFWCapableBuffer#changeRenderingThreadToCurrent()} and {@link GLFWCapableBuffer#unbindContextFromThread()}
|
||||||
|
*
|
||||||
|
* <hr>
|
||||||
|
* Resources created in constructor are:
|
||||||
|
* <ul>
|
||||||
|
* <li>Framebuffer</li>
|
||||||
|
* <li>Color texture (if default fbo initialization chosen)</li>
|
||||||
|
* <li>Depth24 Stencil8 render buffer (if default fbo initialization chosen)</li>
|
||||||
|
* <li>Post processing shader program</li>
|
||||||
|
* <li>Palette texture</li>
|
||||||
|
* <li>Screen quad VAO</li>
|
||||||
|
* <li>Screen quad index buffer</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* The constructor also puts the given buffer in map color mode.
|
||||||
|
*/
|
||||||
|
public class MapColorRenderer implements Runnable {
|
||||||
|
|
||||||
|
private final int fboID;
|
||||||
|
private final GLFWCapableBuffer framebuffer;
|
||||||
|
private final Runnable renderCode;
|
||||||
|
private final int colorTextureID;
|
||||||
|
private final int width;
|
||||||
|
private final int height;
|
||||||
|
private final int renderShader;
|
||||||
|
private final int screenQuadIndices;
|
||||||
|
private int paletteTexture;
|
||||||
|
private float paletteSize;
|
||||||
|
private final int screenQuadVAO;
|
||||||
|
|
||||||
|
public MapColorRenderer(GLFWCapableBuffer framebuffer, Runnable renderCode) {
|
||||||
|
this(framebuffer, renderCode, MapColorRenderer.defaultFramebuffer(framebuffer.width(), framebuffer.height()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapColorRenderer(GLFWCapableBuffer framebuffer, Runnable renderCode, FboInitialization fboInitialization) {
|
||||||
|
this.framebuffer = framebuffer;
|
||||||
|
this.framebuffer.useMapColors();
|
||||||
|
|
||||||
|
this.renderCode = renderCode;
|
||||||
|
this.width = framebuffer.width();
|
||||||
|
this.height = framebuffer.height();
|
||||||
|
|
||||||
|
this.fboID = glGenFramebuffers();
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fboID);
|
||||||
|
this.colorTextureID = fboInitialization.initFbo(fboID);
|
||||||
|
|
||||||
|
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
|
throw new RuntimeException("Framebuffer is not complete!");
|
||||||
|
}
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
// create post-process shader
|
||||||
|
this.renderShader = glCreateProgram();
|
||||||
|
int vertexShader = createShader("/shaders/mapcolorconvert.vertex.glsl", GL_VERTEX_SHADER);
|
||||||
|
int fragmentShader = createShader("/shaders/mapcolorconvert.fragment.glsl", GL_FRAGMENT_SHADER);
|
||||||
|
glAttachShader(renderShader, vertexShader);
|
||||||
|
glAttachShader(renderShader, fragmentShader);
|
||||||
|
glLinkProgram(renderShader);
|
||||||
|
if(glGetProgrami(renderShader, GL_LINK_STATUS) == 0) {
|
||||||
|
throw new RuntimeException("Link error: "+glGetProgramInfoLog(renderShader));
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPalette("palette");
|
||||||
|
|
||||||
|
// create screen quad VAO
|
||||||
|
screenQuadVAO = glGenBuffers();
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, screenQuadVAO);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, new float[] {
|
||||||
|
-1f, -1f,
|
||||||
|
1f, -1f,
|
||||||
|
1f, 1f,
|
||||||
|
-1f, 1f
|
||||||
|
}, GL_STATIC_DRAW);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
screenQuadIndices = glGenBuffers();
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screenQuadIndices);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, new int[] {0,1,2, 2,3,0}, GL_STATIC_DRAW);
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
int paletteSizeUniform = glGetUniformLocation(renderShader, "paletteSize");
|
||||||
|
int paletteUniform = glGetUniformLocation(renderShader, "palette");
|
||||||
|
int frameUniform = glGetUniformLocation(renderShader, "frame");
|
||||||
|
|
||||||
|
glUseProgram(renderShader); {
|
||||||
|
glUniform1i(frameUniform, 0); // texture unit 0
|
||||||
|
glUniform1i(paletteUniform, 1); // texture unit 1
|
||||||
|
glUniform1f(paletteSizeUniform, paletteSize);
|
||||||
|
}
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FboInitialization defaultFramebuffer(int width, int height) {
|
||||||
|
return fboId -> defaultFramebufferInit(fboId, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int defaultFramebufferInit(int fbo, int width, int height) {
|
||||||
|
// color
|
||||||
|
int colorTexture = glGenTextures();
|
||||||
|
glBindTexture(GL_TEXTURE_2D, colorTexture);
|
||||||
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0L);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
// attach to framebuffer
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
// depth
|
||||||
|
int depthStencilBuffer = glGenRenderbuffers();
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilBuffer);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||||
|
return colorTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
// run user code inside of framebuffer
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fboID);
|
||||||
|
renderCode.run();
|
||||||
|
|
||||||
|
// run post processing to display on screen
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
glClearColor(0f, 0f, 0f, 1f); // 0 on RED channel makes maps use NONE
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, colorTextureID);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, paletteTexture);
|
||||||
|
|
||||||
|
glUseProgram(renderShader); {
|
||||||
|
// render post processing quad
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, screenQuadVAO);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(0, 2, GL_FLOAT, false, 2*4, 0); // position
|
||||||
|
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screenQuadIndices);
|
||||||
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
glUseProgram(0);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees OpenGL resources used by this renderer.
|
||||||
|
* You should NOT render with this renderer after this call.
|
||||||
|
*/
|
||||||
|
public void cleanupResources() {
|
||||||
|
glDeleteFramebuffers(fboID);
|
||||||
|
glDeleteProgram(renderShader);
|
||||||
|
glDeleteTextures(paletteTexture);
|
||||||
|
// TODO: more cleanup
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPalette(String filename) {
|
||||||
|
int tex = glGenTextures();
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex);
|
||||||
|
BufferedImage image;
|
||||||
|
try {
|
||||||
|
image = ImageIO.read(MapColorRenderer.class.getResourceAsStream("/textures/"+filename+".png"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException("Missing image "+filename, e);
|
||||||
|
}
|
||||||
|
ByteBuffer pixels = BufferUtils.createByteBuffer(image.getWidth()*image.getHeight()*4);
|
||||||
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
|
int rgb = image.getRGB(x, y);
|
||||||
|
int alpha = (rgb >> 24) & 0xFF;
|
||||||
|
int red = (rgb >> 16) & 0xFF;
|
||||||
|
int green = (rgb >> 8) & 0xFF;
|
||||||
|
int blue = rgb & 0xFF;
|
||||||
|
pixels.put((byte) red);
|
||||||
|
pixels.put((byte) green);
|
||||||
|
pixels.put((byte) blue);
|
||||||
|
pixels.put((byte) alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pixels.flip();
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.getWidth(), image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||||
|
|
||||||
|
// closest neighbor required here, as pixels can have very different rgb values, and interpolation will break palette lookup
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
this.paletteTexture = tex;
|
||||||
|
this.paletteSize = image.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int createShader(String filename, int type) {
|
||||||
|
int shader = glCreateShader(type);
|
||||||
|
try(BufferedReader reader = new BufferedReader(new InputStreamReader(MapColorRenderer.class.getResourceAsStream(filename)))) {
|
||||||
|
String source = reader.lines().collect(Collectors.joining("\n"));
|
||||||
|
glShaderSource(shader, source);
|
||||||
|
glCompileShader(shader);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FboInitialization {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the given framebuffer
|
||||||
|
* @param fboId
|
||||||
|
* @return the texture ID of the color texture, used for post processing.
|
||||||
|
*/
|
||||||
|
int initFbo(int fboId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5,29 +5,8 @@ in vec2 uv;
|
|||||||
out vec4 fragColor;
|
out vec4 fragColor;
|
||||||
|
|
||||||
uniform sampler2D box;
|
uniform sampler2D box;
|
||||||
uniform sampler2D palette;
|
|
||||||
uniform float paletteSize;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 vertexColor = texture(box, uv).rgb;
|
vec3 vertexColor = texture(box, uv).rgb;
|
||||||
|
fragColor = vec4(vertexColor, 1.0);
|
||||||
|
|
||||||
// render in map colors
|
|
||||||
int closest = 0;
|
|
||||||
uint closestDistance = uint(2147483647);
|
|
||||||
for(int i = 4; i < paletteSize; i++) {
|
|
||||||
vec3 mapColor = texture(palette, vec2((i+0.5f)/paletteSize, 0.0)).rgb;
|
|
||||||
int dr = int((mapColor.r - vertexColor.r)*255);
|
|
||||||
int dg = int((mapColor.g - vertexColor.g)*255);
|
|
||||||
int db = int((mapColor.b - vertexColor.b)*255);
|
|
||||||
|
|
||||||
uint d = uint(dr*dr)+uint(dg*dg)+uint(db*db);
|
|
||||||
if(d < closestDistance) {
|
|
||||||
closestDistance = d;
|
|
||||||
closest = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fragColor = vec4(closest/255.0, closest/255.0, closest/255.0, 1.0);
|
|
||||||
//fragColor = vec4(vertexColor, 1.0);
|
|
||||||
}
|
}
|
32
src/lwjgl/resources/shaders/mapcolorconvert.fragment.glsl
Normal file
32
src/lwjgl/resources/shaders/mapcolorconvert.fragment.glsl
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec2 fragCoords;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform sampler2D frame;
|
||||||
|
uniform sampler2D palette;
|
||||||
|
uniform float paletteSize;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = fragCoords;
|
||||||
|
uv.y = -uv.y;
|
||||||
|
vec3 fragmentColor = texture(frame, uv).rgb;
|
||||||
|
|
||||||
|
// render in map colors
|
||||||
|
int closest = 0;
|
||||||
|
uint closestDistance = uint(2147483647);
|
||||||
|
for(int i = 4; i < paletteSize; i++) {
|
||||||
|
vec3 mapColor = texture(palette, vec2((i+0.5f)/paletteSize, 0.0)).rgb;
|
||||||
|
int dr = int((mapColor.r - fragmentColor.r)*255);
|
||||||
|
int dg = int((mapColor.g - fragmentColor.g)*255);
|
||||||
|
int db = int((mapColor.b - fragmentColor.b)*255);
|
||||||
|
|
||||||
|
uint d = uint(dr*dr)+uint(dg*dg)+uint(db*db);
|
||||||
|
if(d < closestDistance) {
|
||||||
|
closestDistance = d;
|
||||||
|
closest = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragColor = vec4(closest/255.0, closest/255.0, closest/255.0, 1.0);
|
||||||
|
}
|
10
src/lwjgl/resources/shaders/mapcolorconvert.vertex.glsl
Normal file
10
src/lwjgl/resources/shaders/mapcolorconvert.vertex.glsl
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 pos;
|
||||||
|
|
||||||
|
out vec2 fragCoords;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
fragCoords = (pos+vec2(1.0))/2.0;
|
||||||
|
gl_Position = vec4(pos, 0.0, 1.0);
|
||||||
|
}
|
@ -94,6 +94,10 @@ public class MinecraftServer {
|
|||||||
@Setter
|
@Setter
|
||||||
private static boolean fixLighting = true;
|
private static boolean fixLighting = true;
|
||||||
|
|
||||||
|
//Rate Limiting
|
||||||
|
@Getter @Setter
|
||||||
|
private static int rateLimit = 0;
|
||||||
|
|
||||||
// Networking
|
// Networking
|
||||||
private static PacketProcessor packetProcessor;
|
private static PacketProcessor packetProcessor;
|
||||||
private static PacketListenerManager packetListenerManager;
|
private static PacketListenerManager packetListenerManager;
|
||||||
|
@ -339,8 +339,7 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean chunkUnloaded = !ChunkUtils.isLoaded(instance, position.getX(), position.getZ());
|
if (!ChunkUtils.isLoaded(instance, position.getX(), position.getZ())) {
|
||||||
if (chunkUnloaded) {
|
|
||||||
// No update for entities in unloaded chunk
|
// No update for entities in unloaded chunk
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -392,6 +391,11 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
|
|||||||
);
|
);
|
||||||
onGround = CollisionUtils.handlePhysics(this, deltaPos, newPosition, newVelocityOut);
|
onGround = CollisionUtils.handlePhysics(this, deltaPos, newPosition, newVelocityOut);
|
||||||
|
|
||||||
|
// Check chunk
|
||||||
|
if (!ChunkUtils.isLoaded(instance, newPosition.getX(), newPosition.getZ())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// World border collision
|
// World border collision
|
||||||
{
|
{
|
||||||
final WorldBorder worldBorder = instance.getWorldBorder();
|
final WorldBorder worldBorder = instance.getWorldBorder();
|
||||||
@ -461,10 +465,11 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
|
|||||||
for (int y = minY; y <= maxY; y++) {
|
for (int y = minY; y <= maxY; y++) {
|
||||||
for (int x = minX; x <= maxX; x++) {
|
for (int x = minX; x <= maxX; x++) {
|
||||||
for (int z = minZ; z <= maxZ; z++) {
|
for (int z = minZ; z <= maxZ; z++) {
|
||||||
chunkUnloaded = !ChunkUtils.isLoaded(instance, x, z);
|
final Chunk chunk = instance.getChunkAt(x, z);
|
||||||
if (chunkUnloaded)
|
if (!ChunkUtils.isLoaded(chunk))
|
||||||
continue;
|
continue;
|
||||||
final CustomBlock customBlock = instance.getCustomBlock(x, y, z);
|
|
||||||
|
final CustomBlock customBlock = chunk.getCustomBlock(x, y, z);
|
||||||
if (customBlock != null) {
|
if (customBlock != null) {
|
||||||
tmpPosition.setX(x);
|
tmpPosition.setX(x);
|
||||||
tmpPosition.setY(y);
|
tmpPosition.setY(y);
|
||||||
@ -979,7 +984,7 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
|
|||||||
final boolean isPlayer = this instanceof Player;
|
final boolean isPlayer = this instanceof Player;
|
||||||
|
|
||||||
if (isPlayer)
|
if (isPlayer)
|
||||||
((Player) this).onChunkChange(lastChunk, newChunk); // Refresh loaded chunk
|
((Player) this).onChunkChange(newChunk); // Refresh loaded chunk
|
||||||
|
|
||||||
// Refresh entity viewable list
|
// Refresh entity viewable list
|
||||||
final long[] lastVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), MinecraftServer.ENTITY_VIEW_DISTANCE);
|
final long[] lastVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), MinecraftServer.ENTITY_VIEW_DISTANCE);
|
||||||
|
@ -287,6 +287,8 @@ public class Player extends LivingEntity implements CommandSender {
|
|||||||
// Flush all pending packets
|
// Flush all pending packets
|
||||||
playerConnection.flush();
|
playerConnection.flush();
|
||||||
|
|
||||||
|
playerConnection.updateStats();
|
||||||
|
|
||||||
// Process received packets
|
// Process received packets
|
||||||
ClientPlayPacket packet;
|
ClientPlayPacket packet;
|
||||||
while ((packet = packets.poll()) != null) {
|
while ((packet = packets.poll()) != null) {
|
||||||
@ -558,7 +560,8 @@ public class Player extends LivingEntity implements CommandSender {
|
|||||||
viewableChunks.add(chunk);
|
viewableChunks.add(chunk);
|
||||||
chunk.addViewer(this);
|
chunk.addViewer(this);
|
||||||
instance.sendChunk(this, chunk);
|
instance.sendChunk(this, chunk);
|
||||||
updateViewPosition(chunk);
|
if (chunk.getChunkX() == Math.floorDiv((int) getPosition().getX(), 16) && chunk.getChunkZ() == Math.floorDiv((int) getPosition().getZ(), 16))
|
||||||
|
updateViewPosition(chunk);
|
||||||
}
|
}
|
||||||
final boolean isLast = counter.get() == length - 1;
|
final boolean isLast = counter.get() == length - 1;
|
||||||
if (isLast) {
|
if (isLast) {
|
||||||
@ -1166,12 +1169,16 @@ public class Player extends LivingEntity implements CommandSender {
|
|||||||
* It does remove and add the player from the chunks viewers list when removed or added
|
* It does remove and add the player from the chunks viewers list when removed or added
|
||||||
* It also calls the events {@link PlayerChunkUnloadEvent} and {@link PlayerChunkLoadEvent}
|
* It also calls the events {@link PlayerChunkUnloadEvent} and {@link PlayerChunkLoadEvent}
|
||||||
*
|
*
|
||||||
* @param lastChunk the last player chunk
|
|
||||||
* @param newChunk the current/new player chunk
|
* @param newChunk the current/new player chunk
|
||||||
*/
|
*/
|
||||||
protected void onChunkChange(Chunk lastChunk, Chunk newChunk) {
|
protected void onChunkChange(Chunk newChunk) {
|
||||||
final long[] lastVisibleChunks = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), MinecraftServer.CHUNK_VIEW_DISTANCE);
|
final long[] lastVisibleChunks = new long[viewableChunks.size()];
|
||||||
final long[] updatedVisibleChunks = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), MinecraftServer.CHUNK_VIEW_DISTANCE);
|
Chunk[] lastViewableChunks = viewableChunks.toArray(new Chunk[0]);
|
||||||
|
for (int i = 0; i < lastViewableChunks.length; i++) {
|
||||||
|
Chunk lastViewableChunk = lastViewableChunks[i];
|
||||||
|
lastVisibleChunks[i] = ChunkUtils.getChunkIndex(lastViewableChunk.getChunkX(), lastViewableChunk.getChunkZ());
|
||||||
|
}
|
||||||
|
final long[] updatedVisibleChunks = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), getChunkRange());
|
||||||
final int[] oldChunks = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunks, updatedVisibleChunks);
|
final int[] oldChunks = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunks, updatedVisibleChunks);
|
||||||
final int[] newChunks = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunks, lastVisibleChunks);
|
final int[] newChunks = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunks, lastVisibleChunks);
|
||||||
|
|
||||||
@ -1184,6 +1191,7 @@ public class Player extends LivingEntity implements CommandSender {
|
|||||||
playerConnection.sendPacket(unloadChunkPacket);
|
playerConnection.sendPacket(unloadChunkPacket);
|
||||||
|
|
||||||
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
|
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
|
||||||
|
viewableChunks.remove(chunk);
|
||||||
if (chunk != null)
|
if (chunk != null)
|
||||||
chunk.removeViewer(this);
|
chunk.removeViewer(this);
|
||||||
}
|
}
|
||||||
|
@ -650,7 +650,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
|
|||||||
* @param z the Z position
|
* @param z the Z position
|
||||||
* @return the chunk at the given position, null if not loaded
|
* @return the chunk at the given position, null if not loaded
|
||||||
*/
|
*/
|
||||||
public Chunk getChunkAt(double x, double z) {
|
public Chunk getChunkAt(float x, float z) {
|
||||||
final int chunkX = ChunkUtils.getChunkCoordinate((int) x);
|
final int chunkX = ChunkUtils.getChunkCoordinate((int) x);
|
||||||
final int chunkZ = ChunkUtils.getChunkCoordinate((int) z);
|
final int chunkZ = ChunkUtils.getChunkCoordinate((int) z);
|
||||||
return getChunk(chunkX, chunkZ);
|
return getChunk(chunkX, chunkZ);
|
||||||
|
@ -508,14 +508,18 @@ public class InventoryClickProcessor {
|
|||||||
slot = slot - inventorySlot + PlayerInventoryUtils.OFFSET;
|
slot = slot - inventorySlot + PlayerInventoryUtils.OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
if (clicked != null) {
|
||||||
|
clicked.onInventoryClick(player, clickType, slot, isPlayerInventory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reset the didCloseInventory field
|
// Reset the didCloseInventory field
|
||||||
// Wait for inventory conditions + events to possibly close the inventory
|
// Wait for inventory conditions + events to possibly close the inventory
|
||||||
player.UNSAFE_changeDidCloseInventory(false);
|
player.UNSAFE_changeDidCloseInventory(false);
|
||||||
|
|
||||||
final List<InventoryCondition> inventoryConditions = isPlayerInventory ?
|
// PreClickEvent
|
||||||
player.getInventory().getInventoryConditions() : inventory.getInventoryConditions();
|
{
|
||||||
if (!inventoryConditions.isEmpty()) {
|
|
||||||
|
|
||||||
InventoryPreClickEvent inventoryPreClickEvent = new InventoryPreClickEvent(player, inventory, slot, clickType, clicked, cursor);
|
InventoryPreClickEvent inventoryPreClickEvent = new InventoryPreClickEvent(player, inventory, slot, clickType, clicked, cursor);
|
||||||
player.callEvent(InventoryPreClickEvent.class, inventoryPreClickEvent);
|
player.callEvent(InventoryPreClickEvent.class, inventoryPreClickEvent);
|
||||||
cursor = inventoryPreClickEvent.getCursorItem();
|
cursor = inventoryPreClickEvent.getCursorItem();
|
||||||
@ -525,6 +529,12 @@ public class InventoryClickProcessor {
|
|||||||
if (inventoryPreClickEvent.isCancelled()) {
|
if (inventoryPreClickEvent.isCancelled()) {
|
||||||
clickResult.setRefresh(true);
|
clickResult.setRefresh(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inventory conditions
|
||||||
|
final List<InventoryCondition> inventoryConditions = isPlayerInventory ?
|
||||||
|
player.getInventory().getInventoryConditions() : inventory.getInventoryConditions();
|
||||||
|
if (!inventoryConditions.isEmpty()) {
|
||||||
|
|
||||||
for (InventoryCondition inventoryCondition : inventoryConditions) {
|
for (InventoryCondition inventoryCondition : inventoryConditions) {
|
||||||
InventoryConditionResult result = new InventoryConditionResult(clicked, cursor);
|
InventoryConditionResult result = new InventoryConditionResult(clicked, cursor);
|
||||||
|
@ -3,10 +3,14 @@ package net.minestom.server.item;
|
|||||||
import net.minestom.server.chat.ColoredText;
|
import net.minestom.server.chat.ColoredText;
|
||||||
import net.minestom.server.data.Data;
|
import net.minestom.server.data.Data;
|
||||||
import net.minestom.server.data.DataContainer;
|
import net.minestom.server.data.DataContainer;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.inventory.click.ClickType;
|
||||||
import net.minestom.server.item.attribute.ItemAttribute;
|
import net.minestom.server.item.attribute.ItemAttribute;
|
||||||
import net.minestom.server.item.metadata.*;
|
import net.minestom.server.item.metadata.*;
|
||||||
import net.minestom.server.item.rule.VanillaStackingRule;
|
import net.minestom.server.item.rule.VanillaStackingRule;
|
||||||
import net.minestom.server.registry.Registries;
|
import net.minestom.server.registry.Registries;
|
||||||
|
import net.minestom.server.utils.BlockPosition;
|
||||||
|
import net.minestom.server.utils.Direction;
|
||||||
import net.minestom.server.utils.NBTUtils;
|
import net.minestom.server.utils.NBTUtils;
|
||||||
import net.minestom.server.utils.validate.Check;
|
import net.minestom.server.utils.validate.Check;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||||
@ -613,4 +617,51 @@ public class ItemStack implements DataContainer {
|
|||||||
}
|
}
|
||||||
return compound;
|
return compound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Callback events
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the player right clicks with this item
|
||||||
|
*
|
||||||
|
* @param player the player who used the item
|
||||||
|
* @param hand the hand used
|
||||||
|
*/
|
||||||
|
public void onRightClick(Player player, Player.Hand hand) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the player left clicks with this item
|
||||||
|
*
|
||||||
|
* @param player the player who used the item
|
||||||
|
* @param hand the hand used
|
||||||
|
*/
|
||||||
|
public void onLeftClick(Player player, Player.Hand hand) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the player right clicks with this item on a block
|
||||||
|
*
|
||||||
|
* @param player the player who used the item
|
||||||
|
* @param hand the hand used
|
||||||
|
* @param position the position of the interacted block
|
||||||
|
* @param blockFace the block face
|
||||||
|
* @return true if it prevents normal item use (placing blocks for instance)
|
||||||
|
*/
|
||||||
|
public boolean onUseOnBlock(Player player, Player.Hand hand, BlockPosition position, Direction blockFace) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the player click on this item on an inventory
|
||||||
|
* <p>
|
||||||
|
* Executed before any events
|
||||||
|
*
|
||||||
|
* @param player the player who clicked on the item
|
||||||
|
* @param clickType the click type
|
||||||
|
* @param slot the slot clicked
|
||||||
|
* @param playerInventory true if the click is in the player inventory
|
||||||
|
*/
|
||||||
|
public void onInventoryClick(Player player, ClickType clickType, int slot, boolean playerInventory) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,15 @@ package net.minestom.server.listener;
|
|||||||
|
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.event.player.PlayerHandAnimationEvent;
|
import net.minestom.server.event.player.PlayerHandAnimationEvent;
|
||||||
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.network.packet.client.play.ClientAnimationPacket;
|
import net.minestom.server.network.packet.client.play.ClientAnimationPacket;
|
||||||
|
|
||||||
public class AnimationListener {
|
public class AnimationListener {
|
||||||
|
|
||||||
public static void animationListener(ClientAnimationPacket packet, Player player) {
|
public static void animationListener(ClientAnimationPacket packet, Player player) {
|
||||||
final Player.Hand hand = packet.hand;
|
final Player.Hand hand = packet.hand;
|
||||||
|
final ItemStack itemStack = player.getItemInHand(hand);
|
||||||
|
itemStack.onLeftClick(player, hand);
|
||||||
PlayerHandAnimationEvent handAnimationEvent = new PlayerHandAnimationEvent(player, hand);
|
PlayerHandAnimationEvent handAnimationEvent = new PlayerHandAnimationEvent(player, hand);
|
||||||
player.callCancellableEvent(PlayerHandAnimationEvent.class, handAnimationEvent, () -> {
|
player.callCancellableEvent(PlayerHandAnimationEvent.class, handAnimationEvent, () -> {
|
||||||
switch (hand) {
|
switch (hand) {
|
||||||
|
@ -20,6 +20,7 @@ import net.minestom.server.item.ItemStack;
|
|||||||
import net.minestom.server.item.Material;
|
import net.minestom.server.item.Material;
|
||||||
import net.minestom.server.network.packet.client.play.ClientPlayerBlockPlacementPacket;
|
import net.minestom.server.network.packet.client.play.ClientPlayerBlockPlacementPacket;
|
||||||
import net.minestom.server.utils.BlockPosition;
|
import net.minestom.server.utils.BlockPosition;
|
||||||
|
import net.minestom.server.utils.Direction;
|
||||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -31,13 +32,19 @@ public class BlockPlacementListener {
|
|||||||
final Player.Hand hand = packet.hand;
|
final Player.Hand hand = packet.hand;
|
||||||
final BlockFace blockFace = packet.blockFace;
|
final BlockFace blockFace = packet.blockFace;
|
||||||
final BlockPosition blockPosition = packet.blockPosition;
|
final BlockPosition blockPosition = packet.blockPosition;
|
||||||
|
final Direction direction = blockFace.toDirection();
|
||||||
|
|
||||||
final Instance instance = player.getInstance();
|
final Instance instance = player.getInstance();
|
||||||
if (instance == null)
|
if (instance == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
final ItemStack usedItem = player.getItemInHand(hand);
|
||||||
|
|
||||||
// Interact at block
|
// Interact at block
|
||||||
|
final boolean cancel = usedItem.onUseOnBlock(player, hand, blockPosition, direction);
|
||||||
PlayerBlockInteractEvent playerBlockInteractEvent = new PlayerBlockInteractEvent(player, blockPosition, hand, blockFace);
|
PlayerBlockInteractEvent playerBlockInteractEvent = new PlayerBlockInteractEvent(player, blockPosition, hand, blockFace);
|
||||||
|
playerBlockInteractEvent.setCancelled(cancel);
|
||||||
|
playerBlockInteractEvent.setBlockingItemUse(cancel);
|
||||||
player.callCancellableEvent(PlayerBlockInteractEvent.class, playerBlockInteractEvent, () -> {
|
player.callCancellableEvent(PlayerBlockInteractEvent.class, playerBlockInteractEvent, () -> {
|
||||||
final CustomBlock customBlock = instance.getCustomBlock(blockPosition);
|
final CustomBlock customBlock = instance.getCustomBlock(blockPosition);
|
||||||
if (customBlock != null) {
|
if (customBlock != null) {
|
||||||
@ -54,7 +61,6 @@ public class BlockPlacementListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if item at hand is a block
|
// Check if item at hand is a block
|
||||||
final ItemStack usedItem = hand == Player.Hand.MAIN ? playerInventory.getItemInMainHand() : playerInventory.getItemInOffHand();
|
|
||||||
final Material material = usedItem.getMaterial();
|
final Material material = usedItem.getMaterial();
|
||||||
if (material == Material.AIR) {
|
if (material == Material.AIR) {
|
||||||
return;
|
return;
|
||||||
@ -123,7 +129,7 @@ public class BlockPlacementListener {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Player didn't try to place a block but interacted with one
|
// Player didn't try to place a block but interacted with one
|
||||||
PlayerUseItemOnBlockEvent event = new PlayerUseItemOnBlockEvent(player, hand, usedItem, blockPosition, blockFace.toDirection());
|
PlayerUseItemOnBlockEvent event = new PlayerUseItemOnBlockEvent(player, hand, usedItem, blockPosition, direction);
|
||||||
player.callEvent(PlayerUseItemOnBlockEvent.class, event);
|
player.callEvent(PlayerUseItemOnBlockEvent.class, event);
|
||||||
refreshChunk = true;
|
refreshChunk = true;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ public class UseItemListener {
|
|||||||
final PlayerInventory inventory = player.getInventory();
|
final PlayerInventory inventory = player.getInventory();
|
||||||
final Player.Hand hand = packet.hand;
|
final Player.Hand hand = packet.hand;
|
||||||
final ItemStack itemStack = hand == Player.Hand.MAIN ? inventory.getItemInMainHand() : inventory.getItemInOffHand();
|
final ItemStack itemStack = hand == Player.Hand.MAIN ? inventory.getItemInMainHand() : inventory.getItemInOffHand();
|
||||||
|
itemStack.onRightClick(player, hand);
|
||||||
PlayerUseItemEvent useItemEvent = new PlayerUseItemEvent(player, hand, itemStack);
|
PlayerUseItemEvent useItemEvent = new PlayerUseItemEvent(player, hand, itemStack);
|
||||||
player.callEvent(PlayerUseItemEvent.class, useItemEvent);
|
player.callEvent(PlayerUseItemEvent.class, useItemEvent);
|
||||||
|
|
||||||
|
@ -46,6 +46,9 @@ public class PacketProcessor {
|
|||||||
channel, c -> new NettyPlayerConnection((SocketChannel) channel.channel())
|
channel, c -> new NettyPlayerConnection((SocketChannel) channel.channel())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (MinecraftServer.getRateLimit() > 0)
|
||||||
|
playerConnection.getPacketCounter().incrementAndGet();
|
||||||
|
|
||||||
final ConnectionState connectionState = playerConnection.getConnectionState();
|
final ConnectionState connectionState = playerConnection.getConnectionState();
|
||||||
|
|
||||||
//if (!printBlackList.contains(id)) {
|
//if (!printBlackList.contains(id)) {
|
||||||
|
@ -59,6 +59,7 @@ public class NettyPlayerConnection extends PlayerConnection {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writePacket(ByteBuf buffer, boolean copy) {
|
public void writePacket(ByteBuf buffer, boolean copy) {
|
||||||
|
//System.out.println(getConnectionState() + " out");
|
||||||
if ((encrypted || compressed) && copy) {
|
if ((encrypted || compressed) && copy) {
|
||||||
buffer = buffer.copy();
|
buffer = buffer.copy();
|
||||||
buffer.retain();
|
buffer.retain();
|
||||||
|
@ -3,11 +3,16 @@ package net.minestom.server.network.player;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import net.minestom.server.chat.ColoredText;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.network.ConnectionState;
|
import net.minestom.server.network.ConnectionState;
|
||||||
import net.minestom.server.network.packet.server.ServerPacket;
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
|
import net.minestom.server.network.packet.server.login.LoginDisconnect;
|
||||||
|
import net.minestom.server.network.packet.server.play.DisconnectPacket;
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A PlayerConnection is an object needed for all created player
|
* A PlayerConnection is an object needed for all created player
|
||||||
@ -27,11 +32,40 @@ public abstract class PlayerConnection {
|
|||||||
private ConnectionState connectionState;
|
private ConnectionState connectionState;
|
||||||
private boolean online;
|
private boolean online;
|
||||||
|
|
||||||
|
private static final ColoredText rateLimitKickMessage = ColoredText.of("Too Many Packets");
|
||||||
|
|
||||||
|
//Connection Stats
|
||||||
|
@Getter
|
||||||
|
private final AtomicInteger packetCounter = new AtomicInteger(0);
|
||||||
|
private short tickCounter = 0;
|
||||||
|
|
||||||
public PlayerConnection() {
|
public PlayerConnection() {
|
||||||
this.online = true;
|
this.online = true;
|
||||||
this.connectionState = ConnectionState.UNKNOWN;
|
this.connectionState = ConnectionState.UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateStats() {
|
||||||
|
if (MinecraftServer.getRateLimit() > 0) {
|
||||||
|
tickCounter++;
|
||||||
|
if (tickCounter % 20 == 0 && tickCounter > 0) {
|
||||||
|
tickCounter = 0;
|
||||||
|
int i = packetCounter.get();
|
||||||
|
packetCounter.set(0);
|
||||||
|
if (i > MinecraftServer.getRateLimit()) {
|
||||||
|
if (connectionState == ConnectionState.LOGIN) {
|
||||||
|
sendPacket(new LoginDisconnect("Too Many Packets"));
|
||||||
|
} else {
|
||||||
|
DisconnectPacket disconnectPacket = new DisconnectPacket();
|
||||||
|
disconnectPacket.message = rateLimitKickMessage;
|
||||||
|
sendPacket(disconnectPacket);
|
||||||
|
}
|
||||||
|
disconnect();
|
||||||
|
refreshOnline(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void enableCompression(int threshold);
|
public abstract void enableCompression(int threshold);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,9 +109,7 @@ public class PerGroupChunkProvider extends ThreadProvider {
|
|||||||
|
|
||||||
ArrayList<Future<?>> futures = new ArrayList<>();
|
ArrayList<Future<?>> futures = new ArrayList<>();
|
||||||
|
|
||||||
instanceInstanceMap.entrySet().forEach(entry -> {
|
instanceInstanceMap.forEach((instance, instanceMap) -> {
|
||||||
final Instance instance = entry.getKey();
|
|
||||||
final Map<LongSet, Instance> instanceMap = entry.getValue();
|
|
||||||
|
|
||||||
// True if the instance ended its tick call
|
// True if the instance ended its tick call
|
||||||
AtomicBoolean instanceUpdated = new AtomicBoolean(false);
|
AtomicBoolean instanceUpdated = new AtomicBoolean(false);
|
||||||
@ -157,7 +155,7 @@ public class PerGroupChunkProvider extends ThreadProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Map<LongSet, Instance> getInstanceMap(Instance instance) {
|
private Map<LongSet, Instance> getInstanceMap(Instance instance) {
|
||||||
return instanceInstanceMap.computeIfAbsent(instance, inst -> new HashMap<>());
|
return instanceInstanceMap.computeIfAbsent(instance, inst -> new ConcurrentHashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -70,4 +70,12 @@ public final class MathUtils {
|
|||||||
public static float setBetween(float number, float min, float max) {
|
public static float setBetween(float number, float min, float max) {
|
||||||
return number > max ? max : number < min ? min : number;
|
return number > max ? max : number < min ? min : number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int clamp(int value, int min, int max) {
|
||||||
|
if (value < min) {
|
||||||
|
return min;
|
||||||
|
} else {
|
||||||
|
return Math.min(value, max);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user