diff --git a/build.gradle b/build.gradle
index fc4d863..0eeddb5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,13 +9,20 @@ allprojects {
repositories {
mavenCentral()
+ maven {
+ url 'https://oss.sonatype.org/content/groups/public/'
+ }
}
}
subprojects {
+ group 'mc'
+
ext {
- slf4j_version = '1.7.21'
- spring_version = '4.2.5.RELEASE'
+ slf4j_version = '1.7.25'
+ spring_version = '5.1.0.RELEASE'
+ lombok_version = '1.18.2'
+ junit_version = '5.3.1'
}
configurations {
@@ -29,20 +36,22 @@ subprojects {
compile (group: 'org.slf4j', name: 'jcl-over-slf4j', version: slf4j_version)
/* Spring */
- compile (group: 'org.springframework', name: 'spring-context', version: spring_version) {
- exclude group: 'commons-logging'
- }
+ compile (group: 'org.springframework', name: 'spring-context', version: spring_version)
/* Lombok */
- annotationProcessor (group: 'org.projectlombok', name: 'lombok', version: '1.16.16')
- compileOnly (group: 'org.projectlombok', name: 'lombok', version: '1.16.16')
+ annotationProcessor (group: 'org.projectlombok', name: 'lombok', version: lombok_version)
+ compileOnly (group: 'org.projectlombok', name: 'lombok', version: lombok_version)
- /* JUnit */
- testCompile (group: 'junit', name: 'junit', version: '4.12')
- /* Simple log */
+ /* Testing */
+ testImplementation (group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: junit_version)
+ testRuntimeOnly(group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: junit_version)
testCompile (group: 'org.slf4j', name: 'slf4j-simple', version: slf4j_version)
- /* Mockito */
- testCompile (group: 'org.mockito', name: 'mockito-core', version: '1.9.5')
+ testCompile (group: 'org.mockito', name: 'mockito-core', version: '1.10.19')
+ testCompile (group: 'org.springframework', name: 'spring-test', version: spring_version)
+ }
+
+ test {
+ useJUnitPlatform()
}
task copyDep(type: Copy) {
diff --git a/core/README.MD b/core/README.MD
index 6176db3..cd77165 100644
--- a/core/README.MD
+++ b/core/README.MD
@@ -18,20 +18,6 @@ Bean:
```
-### InMemoryPlayerManager
-
-Implements: `mc.core.PlayerManager`
-
-Bean:
-
-```xml
-
-
-
-```
-
-`keepAliveInterval` - как часто (в ms) отправлять клиентам пакет `KeepAlive`
-
### IdleTime
Implements: `mc.core.time.TimeProcessor`
diff --git a/core/build.gradle b/core/build.gradle
index 77e3054..1b923c1 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -1,4 +1,3 @@
-group 'mc'
version '1.0-SNAPSHOT'
apply plugin: 'maven'
@@ -9,7 +8,7 @@ mainClassName = "mc.core.Main"
dependencies {
/* Components */
compile (group: 'commons-io', name: 'commons-io', version: '2.6')
- compile (group: 'com.google.guava', name: 'guava', version: '24.1-jre')
+ compile (group: 'com.google.guava', name: 'guava', version: '26.0-jre')
/* Named Binary Tags */
- compile (group: 'com.flowpowered', name: 'flow-nbt', version: '1.0.0')
+ compile (group: 'com.flowpowered', name: 'flow-nbt', version: '1.0.1-SNAPSHOT')
}
diff --git a/core/src/main/java/mc/core/CoreEventListener.java b/core/src/main/java/mc/core/CoreEventListener.java
new file mode 100644
index 0000000..201b60e
--- /dev/null
+++ b/core/src/main/java/mc/core/CoreEventListener.java
@@ -0,0 +1,85 @@
+package mc.core;
+
+import com.google.common.eventbus.Subscribe;
+import lombok.extern.slf4j.Slf4j;
+import mc.core.eventbus.EventBusGetter;
+import mc.core.eventbus.events.CS_PlayerMoveEvent;
+import mc.core.eventbus.events.SC_ChunkLoadEvent;
+import mc.core.eventbus.events.SC_ChunkUnloadEvent;
+import mc.core.utils.CompactedCoords;
+import mc.core.world.chunk.Chunk;
+
+import javax.annotation.PostConstruct;
+import java.util.Iterator;
+
+@Slf4j
+public class CoreEventListener {
+ @PostConstruct
+ public void registerEventHandlers() {
+ EventBusGetter.getInstance().register(this);
+ }
+
+ @Subscribe
+ public void handlerPlayerMoveEvent(CS_PlayerMoveEvent event) {
+ log.trace("(GameLoop) playerMoveEventHandler()");
+
+ Chunk chunk;
+ chunk = event.getPlayer().getWorld().getChunk(event.getOldLocation()); // Old chunk
+ int ccX = chunk.getX();
+ int ccZ = chunk.getZ();
+ chunk = event.getPlayer().getWorld().getChunk(event.getNewLocation()); // Next chunk
+ int ncX = chunk.getX();
+ int ncZ = chunk.getZ();
+
+ if (event.isRecalcChunk() || (ncX != ccX || ncZ != ccZ)) {
+ final int viewDistance = event.getPlayer().getSettings().getViewDistance() + 1;
+ int cMinX = chunk.getX() - viewDistance;
+ int cMaxX = chunk.getX() + viewDistance;
+ int cMinZ = chunk.getZ() - viewDistance;
+ int cMaxZ = chunk.getZ() + viewDistance;
+
+ SC_ChunkUnloadEvent eventChunkUnload = new SC_ChunkUnloadEvent(event.getPlayer());
+ Iterator itr = event.getPlayer().getLoadedChunks().iterator();
+ while(itr.hasNext()) {
+ int compressXZ = itr.next();
+ int[] xz = CompactedCoords.uncompressXZ(compressXZ);
+ if (xz[0] > cMaxX || xz[0] < cMinX || xz[1] > cMaxZ || xz[1] < cMinZ) {
+ eventChunkUnload.getNeedUnloadChunks().add(compressXZ);
+ itr.remove();
+ }
+ }
+
+ if (!eventChunkUnload.getNeedUnloadChunks().isEmpty()) {
+ EventBusGetter.getInstance().post(eventChunkUnload);
+ }
+
+ SC_ChunkLoadEvent eventChunkLoad = new SC_ChunkLoadEvent(event.getPlayer());
+ for (int cZ = cMinZ; cZ <= cMaxZ; cZ++) {
+ for (int cX = cMinX; cX <= cMaxX; cX++) {
+ int compressXZ = CompactedCoords.compressXZ(cX, cZ);
+ if (!event.getPlayer().getLoadedChunks().contains(compressXZ)) {
+ if (!event.getPlayer().getLoadedChunks().contains(compressXZ)) {
+ eventChunkLoad.getNeedLoadChunks().add(compressXZ);
+ event.getPlayer().getLoadedChunks().add(compressXZ);
+ }
+ }
+ }
+ }
+
+ if (!eventChunkLoad.getNeedLoadChunks().isEmpty()) {
+ EventBusGetter.getInstance().post(eventChunkLoad);
+ }
+ }
+
+ event.getPlayer().getLocation().setXYZ(
+ event.getNewLocation().getX(),
+ event.getNewLocation().getY(),
+ event.getNewLocation().getZ()
+ );
+
+ // TODO отсылать клиенту только(!) для корректировки позиции
+ // SC_PlayerMoveEvent nextEvent = new SC_PlayerMoveEvent(event.getPlayer());
+ // nextEvent.setNewLocation(event.getNewLocation());
+ // EventBusGetter.INSTANCE.post(nextEvent);
+ }
+}
diff --git a/core/src/main/java/mc/core/EntityLocation.java b/core/src/main/java/mc/core/EntityLocation.java
index 960fd08..0b4e237 100644
--- a/core/src/main/java/mc/core/EntityLocation.java
+++ b/core/src/main/java/mc/core/EntityLocation.java
@@ -1,29 +1,29 @@
-/*
- * DmitriyMX
- * 2018-08-08
- */
package mc.core;
-import lombok.Getter;
-import lombok.Setter;
-import mc.core.exception.ResourceUnloadedException;
-import mc.core.world.World;
-import mc.core.world.chunk.Chunk;
-import mc.core.world.chunk.ChunkSection;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
-
-public class EntityLocation extends Location implements Cloneable {
- @Getter
- @Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Data
+public class EntityLocation implements Cloneable {
+ private double x, y, z;
private float yaw, pitch;
- private Reference refWorld;
- public EntityLocation(double x, double y, double z, float yaw, float pitch, World world) {
- super(x, y, z);
- setYawPitch(yaw, pitch);
- setWorld(world);
+ public static EntityLocation ZERO() {
+ return new EntityLocation(0d,0d,0d,0f,0f);
+ }
+
+ public void set(EntityLocation location) {
+ setXYZ(location.x, location.y, location.z);
+ setYawPitch(location.yaw, location.pitch);
+ }
+
+ public void setXYZ(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
}
public void setYawPitch(float yaw, float pitch) {
@@ -31,44 +31,25 @@ public class EntityLocation extends Location implements Cloneable {
this.pitch = pitch;
}
- public void setYawPitch(EntityLocation entityLocation) {
- setYawPitch(entityLocation.yaw, entityLocation.pitch);
+ public int getBlockX() {
+ return Double.valueOf(Math.floor(x)).intValue();
}
- public World getWorld() {
- if (refWorld == null) {
- return null;
- } else if (refWorld.get() == null) {
- throw new ResourceUnloadedException("World unloaded");
- } else {
- return refWorld.get();
- }
+ public int getBlockY() {
+ return Double.valueOf(Math.floor(y)).intValue();
}
- public void setWorld (World world) {
- this.refWorld = new WeakReference<>(world);
- }
-
- public Chunk getChunk() {
- World world = getWorld();
- if (world == null) {
- return null;
- } else {
- return world.getChunk(getBlockX() >> 4, getBlockZ() >> 4);
- }
- }
-
- public ChunkSection getChunkSection() {
- Chunk chunk = getChunk();
- if (chunk == null) {
- return null;
- } else {
- return chunk.getChunkSection(getBlockY() >> 4);
- }
+ public int getBlockZ() {
+ return Double.valueOf(Math.floor(z)).intValue();
}
@Override
public EntityLocation clone() {
- return (EntityLocation) super.clone();
+ try {
+ return (EntityLocation) super.clone();
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ return null;
+ }
}
}
diff --git a/core/src/main/java/mc/core/GameLoop.java b/core/src/main/java/mc/core/GameLoop.java
index 06fc0a1..4b5affc 100644
--- a/core/src/main/java/mc/core/GameLoop.java
+++ b/core/src/main/java/mc/core/GameLoop.java
@@ -4,22 +4,12 @@
*/
package mc.core;
-import com.google.common.eventbus.Subscribe;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
-import mc.core.events.CS_PlayerMoveEvent;
-import mc.core.events.EventBusGetter;
-import mc.core.events.SC_ChunkLoadEvent;
-import mc.core.events.SC_ChunkUnloadEvent;
import mc.core.player.PlayerManager;
import mc.core.time.TimeProcessor;
-import mc.core.utils.CompactedCoords;
-import mc.core.world.World;
-import mc.core.world.chunk.Chunk;
import org.springframework.beans.factory.annotation.Autowired;
-import java.util.Iterator;
-
@Slf4j
public class GameLoop extends Thread {
private final TpsWatcher TPS_WATCHER = TpsWatcher.getInstance();
@@ -48,77 +38,16 @@ public class GameLoop extends Thread {
TPS_WATCHER.setTraceTPS(value);
}
- @Subscribe
- public void playerMoveEventHandler(CS_PlayerMoveEvent event) {
- log.trace("(GameLoop) playerMoveEventHandler()");
-
- Chunk chunk;
- chunk = event.getOldLocation().getChunk(); // Old chunk
- int ccX = chunk.getX();
- int ccZ = chunk.getZ();
- chunk = event.getNewLocation().getChunk(); // Next chunk
- int ncX = chunk.getX();
- int ncZ = chunk.getZ();
-
- if (event.isRecalcChunk() || (ncX != ccX || ncZ != ccZ)) {
- final int viewDistance = event.getPlayer().getSettings().getViewDistance() + 1;
- int cMinX = chunk.getX() - viewDistance;
- int cMaxX = chunk.getX() + viewDistance;
- int cMinZ = chunk.getZ() - viewDistance;
- int cMaxZ = chunk.getZ() + viewDistance;
-
- SC_ChunkUnloadEvent eventChunkUnload = new SC_ChunkUnloadEvent(event.getPlayer());
- Iterator itr = event.getPlayer().getLoadedChunks().iterator();
- while(itr.hasNext()) {
- int compressXZ = itr.next();
- int[] xz = CompactedCoords.uncompressXZ(compressXZ);
- if (xz[0] > cMaxX || xz[0] < cMinX || xz[1] > cMaxZ || xz[1] < cMinZ) {
- eventChunkUnload.getNeedUnloadChunks().add(compressXZ);
- itr.remove();
- }
- }
-
- if (!eventChunkUnload.getNeedUnloadChunks().isEmpty()) {
- EventBusGetter.INSTANCE.post(eventChunkUnload);
- }
-
- SC_ChunkLoadEvent eventChunkLoad = new SC_ChunkLoadEvent(event.getPlayer());
- for (int cZ = cMinZ; cZ <= cMaxZ; cZ++) {
- for (int cX = cMinX; cX <= cMaxX; cX++) {
- int compressXZ = CompactedCoords.compressXZ(cX, cZ);
- if (!event.getPlayer().getLoadedChunks().contains(compressXZ)) {
- if (!event.getPlayer().getLoadedChunks().contains(compressXZ)) {
- eventChunkLoad.getNeedLoadChunks().add(compressXZ);
- event.getPlayer().getLoadedChunks().add(compressXZ);
- }
- }
- }
- }
-
- if (!eventChunkLoad.getNeedLoadChunks().isEmpty()) {
- EventBusGetter.INSTANCE.post(eventChunkLoad);
- }
- }
-
- event.getPlayer().getLocation().setXYZ(event.getNewLocation());
-
- // TODO отсылать клиенту только(!) для корректировки позиции
-// SC_PlayerMoveEvent nextEvent = new SC_PlayerMoveEvent(event.getPlayer());
-// nextEvent.setNewLocation(event.getNewLocation());
-// EventBusGetter.INSTANCE.post(nextEvent);
- }
-
@Override
public void run() {
TPS_WATCHER.startWatch();
- EventBusGetter.INSTANCE.register(this);
-
while (!isInterrupted()) {
TPS_WATCHER.check();
/* --- --- --- */
+ /* TODO нужно перенести этот функционал на Network */
playerManager.getBroadcastChannel().sendTimeUpdate(
gameTimer.getGameTime(),
gameTimer.getWorldAge()
diff --git a/core/src/main/java/mc/core/ImmutableEntityLocation.java b/core/src/main/java/mc/core/ImmutableEntityLocation.java
new file mode 100644
index 0000000..f71c1e3
--- /dev/null
+++ b/core/src/main/java/mc/core/ImmutableEntityLocation.java
@@ -0,0 +1,57 @@
+package mc.core;
+
+public class ImmutableEntityLocation extends EntityLocation {
+ public ImmutableEntityLocation(double x, double y, double z, float yaw, float pitch) {
+ super(x, y, z, yaw, pitch);
+ }
+
+ public ImmutableEntityLocation(EntityLocation location) {
+ this(
+ location.getX(),
+ location.getY(),
+ location.getZ(),
+ location.getYaw(),
+ location.getPitch()
+ );
+ }
+
+ @Override
+ public void setX(double x) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setY(double y) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setZ(double z) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setYaw(float yaw) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setPitch(float pitch) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void set(EntityLocation location) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setXYZ(double x, double y, double z) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setYawPitch(float yaw, float pitch) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/core/src/main/java/mc/core/Location.java b/core/src/main/java/mc/core/Location.java
deleted file mode 100644
index 62ca57d..0000000
--- a/core/src/main/java/mc/core/Location.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * DmitriyMX
- * 2018-08-08
- */
-package mc.core;
-
-import lombok.Getter;
-import lombok.Setter;
-
-public class Location implements Cloneable {
- @Getter
- @Setter
- private double x, y, z;
-
- public Location (double x, double y, double z) {
- setXYZ(x, y, z);
- }
-
- public void setXYZ(double x, double y, double z) {
- this.x = x;
- this.y = y;
- this.z = z;
- }
-
- public void setXYZ(Location location) {
- setXYZ(location.x, location.y, location.z);
- }
-
- public int getBlockX() {
- return Double.valueOf(Math.floor(x)).intValue();
- }
-
- public int getBlockY() {
- return Double.valueOf(Math.floor(y)).intValue();
- }
-
- public int getBlockZ() {
- return Double.valueOf(Math.floor(z)).intValue();
- }
-
- @Override
- public Location clone() {
- try {
- return (Location) super.clone();
- } catch (CloneNotSupportedException e) { // такое в нашем случае вообще возможно?
- e.printStackTrace();
- return null;
- }
- }
-}
diff --git a/core/src/main/java/mc/core/embedded/FakePlayerManager.java b/core/src/main/java/mc/core/embedded/FakePlayerManager.java
index 612de62..f90255b 100644
--- a/core/src/main/java/mc/core/embedded/FakePlayerManager.java
+++ b/core/src/main/java/mc/core/embedded/FakePlayerManager.java
@@ -1,7 +1,3 @@
-/*
- * DmitriyMX
- * 2018-06-29
- */
package mc.core.embedded;
import mc.core.EntityLocation;
@@ -12,8 +8,7 @@ import mc.core.player.Player;
import mc.core.player.PlayerManager;
import mc.core.text.Text;
import mc.core.text.Title;
-import mc.core.world.chunk.Chunk;
-import mc.core.world.chunk.ChunkSection;
+import mc.core.world.World;
import java.util.Collections;
import java.util.List;
@@ -21,9 +16,6 @@ import java.util.Optional;
public class FakePlayerManager implements PlayerManager {
public static class FakeNetChannet implements NetChannel {
- @Override
- public void sendKeepAlive() {
- }
@Override
public void sendTimeUpdate(long time, long age) {
@@ -53,7 +45,7 @@ public class FakePlayerManager implements PlayerManager {
private static final NetChannel FAKE_NET_CHANNEL = new FakeNetChannet();
@Override
- public Player createPlayer(String name, EntityLocation defaultLocation) {
+ public Player createPlayer(String name, EntityLocation location, World world) {
return null;
}
@@ -66,13 +58,8 @@ public class FakePlayerManager implements PlayerManager {
}
@Override
- public Optional getPlayer(String name) {
- return Optional.empty();
- }
-
- @Override
- public Optional getPlayerById(int id) {
- return Optional.empty();
+ public Player getPlayer(String name) {
+ return null;
}
@Override
@@ -81,7 +68,7 @@ public class FakePlayerManager implements PlayerManager {
}
@Override
- public int getCountOnlinePlayers() {
+ public int getCountPlayers() {
return 0;
}
@@ -89,4 +76,9 @@ public class FakePlayerManager implements PlayerManager {
public NetChannel getBroadcastChannel() {
return FAKE_NET_CHANNEL;
}
+
+ @Override
+ public Player getOfflinePlayer(String name) {
+ return null;
+ }
}
diff --git a/core/src/main/java/mc/core/events/Event.java b/core/src/main/java/mc/core/eventbus/Event.java
similarity index 89%
rename from core/src/main/java/mc/core/events/Event.java
rename to core/src/main/java/mc/core/eventbus/Event.java
index 066e7c8..08aa0ec 100644
--- a/core/src/main/java/mc/core/events/Event.java
+++ b/core/src/main/java/mc/core/eventbus/Event.java
@@ -2,7 +2,7 @@
* DmitriyMX
* 2018-05-02
*/
-package mc.core.events;
+package mc.core.eventbus;
public interface Event {
void setCanceled(boolean value);
diff --git a/core/src/main/java/mc/core/events/EventBase.java b/core/src/main/java/mc/core/eventbus/EventBase.java
similarity index 89%
rename from core/src/main/java/mc/core/events/EventBase.java
rename to core/src/main/java/mc/core/eventbus/EventBase.java
index 8c4f030..7756cce 100644
--- a/core/src/main/java/mc/core/events/EventBase.java
+++ b/core/src/main/java/mc/core/eventbus/EventBase.java
@@ -2,7 +2,7 @@
* DmitriyMX
* 2018-05-02
*/
-package mc.core.events;
+package mc.core.eventbus;
import lombok.Getter;
import lombok.Setter;
diff --git a/core/src/main/java/mc/core/events/EventBusGetter.java b/core/src/main/java/mc/core/eventbus/EventBusGetter.java
similarity index 59%
rename from core/src/main/java/mc/core/events/EventBusGetter.java
rename to core/src/main/java/mc/core/eventbus/EventBusGetter.java
index d2e5aa3..37e952b 100644
--- a/core/src/main/java/mc/core/events/EventBusGetter.java
+++ b/core/src/main/java/mc/core/eventbus/EventBusGetter.java
@@ -2,12 +2,14 @@
* DmitriyMX
* 2018-05-02
*/
-package mc.core.events;
+package mc.core.eventbus;
import com.google.common.eventbus.EventBus;
+import lombok.Getter;
public final class EventBusGetter {
- public static final EventBus INSTANCE = new EventBus();
+ @Getter
+ private static final EventBus instance = new EventBus();
private EventBusGetter() {
}
diff --git a/core/src/main/java/mc/core/events/CS_PlayerMoveEvent.java b/core/src/main/java/mc/core/eventbus/events/CS_PlayerMoveEvent.java
similarity index 51%
rename from core/src/main/java/mc/core/events/CS_PlayerMoveEvent.java
rename to core/src/main/java/mc/core/eventbus/events/CS_PlayerMoveEvent.java
index d44f7b1..4d759ab 100644
--- a/core/src/main/java/mc/core/events/CS_PlayerMoveEvent.java
+++ b/core/src/main/java/mc/core/eventbus/events/CS_PlayerMoveEvent.java
@@ -2,22 +2,26 @@
* DmitriyMX
* 2018-05-02
*/
-package mc.core.events;
+package mc.core.eventbus.events;
import lombok.Getter;
-import lombok.RequiredArgsConstructor;
import lombok.Setter;
import mc.core.EntityLocation;
+import mc.core.ImmutableEntityLocation;
+import mc.core.eventbus.EventBase;
import mc.core.player.Player;
-@RequiredArgsConstructor
@Getter
public class CS_PlayerMoveEvent extends EventBase {
private final Player player;
- private final EntityLocation oldLocation; // TODO сомнительное решение
- // вообще нужно будет создать реализацию "иммутабл локейшен" для подобных ситуаций
+ private final ImmutableEntityLocation oldLocation;
@Setter
private EntityLocation newLocation;
@Setter
private boolean recalcChunk = false;
+
+ public CS_PlayerMoveEvent(Player player, EntityLocation oldLocation) {
+ this.player = player;
+ this.oldLocation = new ImmutableEntityLocation(oldLocation);
+ }
}
diff --git a/core/src/main/java/mc/core/events/SC_ChunkLoadEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_ChunkLoadEvent.java
similarity index 83%
rename from core/src/main/java/mc/core/events/SC_ChunkLoadEvent.java
rename to core/src/main/java/mc/core/eventbus/events/SC_ChunkLoadEvent.java
index 62d35c6..ea55a7a 100644
--- a/core/src/main/java/mc/core/events/SC_ChunkLoadEvent.java
+++ b/core/src/main/java/mc/core/eventbus/events/SC_ChunkLoadEvent.java
@@ -1,7 +1,8 @@
-package mc.core.events;
+package mc.core.eventbus.events;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
+import mc.core.eventbus.EventBase;
import mc.core.player.Player;
import java.util.ArrayList;
diff --git a/core/src/main/java/mc/core/events/SC_ChunkUnloadEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_ChunkUnloadEvent.java
similarity index 83%
rename from core/src/main/java/mc/core/events/SC_ChunkUnloadEvent.java
rename to core/src/main/java/mc/core/eventbus/events/SC_ChunkUnloadEvent.java
index 397cbac..b2cce0d 100644
--- a/core/src/main/java/mc/core/events/SC_ChunkUnloadEvent.java
+++ b/core/src/main/java/mc/core/eventbus/events/SC_ChunkUnloadEvent.java
@@ -1,7 +1,8 @@
-package mc.core.events;
+package mc.core.eventbus.events;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
+import mc.core.eventbus.EventBase;
import mc.core.player.Player;
import java.util.ArrayList;
diff --git a/core/src/main/java/mc/core/events/LoginEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_LoginEvent.java
similarity index 75%
rename from core/src/main/java/mc/core/events/LoginEvent.java
rename to core/src/main/java/mc/core/eventbus/events/SC_LoginEvent.java
index 63e123f..95765f3 100644
--- a/core/src/main/java/mc/core/events/LoginEvent.java
+++ b/core/src/main/java/mc/core/eventbus/events/SC_LoginEvent.java
@@ -2,18 +2,19 @@
* DmitriyMX
* 2018-05-02
*/
-package mc.core.events;
+package mc.core.eventbus.events;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
+import mc.core.eventbus.EventBase;
import java.net.SocketAddress;
@RequiredArgsConstructor
@Getter
@Setter
-public class LoginEvent extends EventBase {
+public class SC_LoginEvent extends EventBase {
private String playerName;
private final SocketAddress remoteAddress;
private boolean deny;
diff --git a/core/src/main/java/mc/core/events/PlayerLookEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_PlayerLookEvent.java
similarity index 72%
rename from core/src/main/java/mc/core/events/PlayerLookEvent.java
rename to core/src/main/java/mc/core/eventbus/events/SC_PlayerLookEvent.java
index 7506530..90e5379 100644
--- a/core/src/main/java/mc/core/events/PlayerLookEvent.java
+++ b/core/src/main/java/mc/core/eventbus/events/SC_PlayerLookEvent.java
@@ -2,18 +2,19 @@
* DmitriyMX
* 2018-05-02
*/
-package mc.core.events;
+package mc.core.eventbus.events;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import mc.core.EntityLocation;
+import mc.core.eventbus.EventBase;
import mc.core.player.Player;
@RequiredArgsConstructor
@Getter
@Setter
-public class PlayerLookEvent extends EventBase {
+public class SC_PlayerLookEvent extends EventBase {
private final Player player;
private EntityLocation newLook;
}
diff --git a/core/src/main/java/mc/core/events/SC_PlayerMoveEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_PlayerMoveEvent.java
similarity index 82%
rename from core/src/main/java/mc/core/events/SC_PlayerMoveEvent.java
rename to core/src/main/java/mc/core/eventbus/events/SC_PlayerMoveEvent.java
index d64350b..d0634a7 100644
--- a/core/src/main/java/mc/core/events/SC_PlayerMoveEvent.java
+++ b/core/src/main/java/mc/core/eventbus/events/SC_PlayerMoveEvent.java
@@ -1,9 +1,10 @@
-package mc.core.events;
+package mc.core.eventbus.events;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import mc.core.EntityLocation;
+import mc.core.eventbus.EventBase;
import mc.core.player.Player;
@RequiredArgsConstructor
diff --git a/core/src/main/java/mc/core/events/ServerPingEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_ServerPingEvent.java
similarity index 74%
rename from core/src/main/java/mc/core/events/ServerPingEvent.java
rename to core/src/main/java/mc/core/eventbus/events/SC_ServerPingEvent.java
index 3bbafce..877df70 100644
--- a/core/src/main/java/mc/core/events/ServerPingEvent.java
+++ b/core/src/main/java/mc/core/eventbus/events/SC_ServerPingEvent.java
@@ -2,18 +2,19 @@
* DmitriyMX
* 2018-05-02
*/
-package mc.core.events;
+package mc.core.eventbus.events;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
+import mc.core.eventbus.EventBase;
import java.net.SocketAddress;
@RequiredArgsConstructor
@Getter
@Setter
-public class ServerPingEvent extends EventBase {
+public class SC_ServerPingEvent extends EventBase {
private final SocketAddress remoteAddress;
private String description;
private int online;
diff --git a/core/src/main/java/mc/core/network/BroadcastNetChannel.java b/core/src/main/java/mc/core/network/BroadcastNetChannel.java
index 3f5e7bb..ed15fab 100644
--- a/core/src/main/java/mc/core/network/BroadcastNetChannel.java
+++ b/core/src/main/java/mc/core/network/BroadcastNetChannel.java
@@ -16,11 +16,6 @@ import java.util.stream.Stream;
public class BroadcastNetChannel implements NetChannel {
private final Stream playerStream;
- @Override
- public void sendKeepAlive() {
- playerStream.forEach(player -> player.getChannel().sendKeepAlive());
- }
-
@Override
public void sendTimeUpdate(final long time, final long age) {
playerStream.forEach(player -> player.getChannel().sendTimeUpdate(time, age));
diff --git a/core/src/main/java/mc/core/network/NetChannel.java b/core/src/main/java/mc/core/network/NetChannel.java
index 69c05c7..bfd6899 100644
--- a/core/src/main/java/mc/core/network/NetChannel.java
+++ b/core/src/main/java/mc/core/network/NetChannel.java
@@ -9,7 +9,6 @@ import mc.core.text.Text;
import mc.core.text.Title;
public interface NetChannel {
- void sendKeepAlive();
void sendTimeUpdate(long time, long age);
default void sendChatMessage(Text text) {
sendChatMessage(text, MessageType.CHAT_MESSAGE);
diff --git a/core/src/main/java/mc/core/network/NetInputStream.java b/core/src/main/java/mc/core/network/NetInputStream.java
index a2e7b54..9ae1049 100644
--- a/core/src/main/java/mc/core/network/NetInputStream.java
+++ b/core/src/main/java/mc/core/network/NetInputStream.java
@@ -22,6 +22,7 @@ public abstract class NetInputStream {
public abstract short readShort();
public abstract int readInt();
public abstract int readVarInt();
+ public abstract int readVarInt(int[] countReadBytes);
public abstract long readLong();
public abstract float readFloat();
public abstract double readDouble();
diff --git a/core/src/main/java/mc/core/player/InMemoryPlayerManager.java b/core/src/main/java/mc/core/player/InMemoryPlayerManager.java
deleted file mode 100644
index d373c59..0000000
--- a/core/src/main/java/mc/core/player/InMemoryPlayerManager.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * DmitriyMX
- * 2018-04-15
- */
-package mc.core.player;
-
-import lombok.Setter;
-import lombok.extern.slf4j.Slf4j;
-import mc.core.Config;
-import mc.core.EntityLocation;
-import mc.core.network.BroadcastNetChannel;
-import mc.core.network.NetChannel;
-import org.springframework.beans.factory.annotation.Autowired;
-
-import java.util.*;
-import java.util.stream.Collectors;
-
-@Slf4j
-public class InMemoryPlayerManager implements PlayerManager, Runnable {
- private static final Random rand = new Random();
- private List players;
- private final Object lock = new Object();
- @Setter
- private int keepAliveInterval = 1;
-
- @Autowired
- public InMemoryPlayerManager(Config config) {
- final int c = config.getMaxPlayers() > 50 ? 50 : config.getMaxPlayers();
- players = Collections.synchronizedList(new ArrayList<>(c));
- (new Thread(this, "KeepAlive")).start();
- }
-
- @Override
- public Player createPlayer(String name, EntityLocation defaultLocation) {
- SimplePlayer player = new SimplePlayer();
- player.setId(rand.nextInt(10000));
- player.setUUID(UUID.nameUUIDFromBytes(name.getBytes()));
- player.setName(name);
- player.getLocation().setXYZ(defaultLocation);
- player.getLocation().setYawPitch(defaultLocation);
- player.getLocation().setWorld(defaultLocation.getWorld());
- player.setSettings(new PlayerSettings());
-
- synchronized (lock) {
- players.add(player);
- lock.notify();
- }
- return player;
- }
-
- @Override
- public void joinServer(Player player) {
- ((SimplePlayer) player).setOnline(true);
- }
-
- @Override
- public void leftServer(Player player) {
- ((SimplePlayer) player).setOnline(false);
- }
-
- @Override
- public Optional getPlayer(final String name) {
- return players.stream()
- .filter(player -> player.getName().equalsIgnoreCase(name))
- .findFirst();
- }
-
- @Override
- public Optional getPlayerById(final int id) {
- return players.stream()
- .filter(player -> player.getId() == id)
- .findFirst();
- }
-
- @Override
- public List getPlayers() {
- return players.stream().filter(Player::isOnline).collect(Collectors.toList());
- }
-
- @Override
- public int getCountOnlinePlayers() {
- return (int) players.stream().filter(Player::isOnline).count();
- }
-
- @Override
- public NetChannel getBroadcastChannel() {
- return new BroadcastNetChannel(players.stream().filter(Player::isOnline));
- }
-
- @Override
- public void run() {
- while (!Thread.currentThread().isInterrupted()) {
- synchronized (lock) {
- while (players.size() == 0) {
- try {
- lock.wait();
- } catch (InterruptedException e) {
- return;
- }
- }
- }
-
- getBroadcastChannel().sendKeepAlive();
-
- try {
- Thread.sleep(keepAliveInterval);
- } catch (InterruptedException e) {
- return;
- }
- }
- }
-}
diff --git a/core/src/main/java/mc/core/player/Player.java b/core/src/main/java/mc/core/player/Player.java
index 11c41a3..eefe499 100644
--- a/core/src/main/java/mc/core/player/Player.java
+++ b/core/src/main/java/mc/core/player/Player.java
@@ -6,13 +6,14 @@ package mc.core.player;
import mc.core.EntityLocation;
import mc.core.network.NetChannel;
+import mc.core.world.World;
import java.util.List;
import java.util.UUID;
public interface Player {
int getId();
- UUID getUUID();
+ UUID getUuid();
String getName();
boolean isOnline();
@@ -23,7 +24,8 @@ public interface Player {
void setChannel(NetChannel channel);
EntityLocation getLocation();
- //TODO надо определиться - нужно ли здесь setLocation() или нет
+ World getWorld();
+ void setWorld(World world);
boolean isFlying();
void setFlying(boolean value);
diff --git a/core/src/main/java/mc/core/player/PlayerManager.java b/core/src/main/java/mc/core/player/PlayerManager.java
index 8e07d6e..4fe5485 100644
--- a/core/src/main/java/mc/core/player/PlayerManager.java
+++ b/core/src/main/java/mc/core/player/PlayerManager.java
@@ -1,22 +1,20 @@
-/*
- * DmitriyMX
- * 2018-04-15
- */
package mc.core.player;
import mc.core.EntityLocation;
import mc.core.network.NetChannel;
+import mc.core.world.World;
import java.util.List;
-import java.util.Optional;
public interface PlayerManager {
- Player createPlayer(String name, EntityLocation defaultLocation);
+ Player createPlayer(String name, EntityLocation location, World world);
void joinServer(Player player);
void leftServer(Player player);
- Optional getPlayer(String name);
- Optional getPlayerById(int id);
+
+ Player getPlayer(String name);
List getPlayers();
- int getCountOnlinePlayers();
+ int getCountPlayers();
NetChannel getBroadcastChannel();
+
+ Player getOfflinePlayer(String name);
}
diff --git a/core/src/main/java/mc/core/player/SimplePlayer.java b/core/src/main/java/mc/core/player/SimplePlayer.java
deleted file mode 100644
index 2472758..0000000
--- a/core/src/main/java/mc/core/player/SimplePlayer.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * DmitriyMX
- * 2018-04-23
- */
-package mc.core.player;
-
-import lombok.Data;
-import mc.core.EntityLocation;
-import mc.core.network.NetChannel;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-@Data
-public class SimplePlayer implements Player {
- private int id;
- private UUID uuid;
- private String name;
- private boolean online = false;
- private NetChannel channel;
- private EntityLocation location = new EntityLocation(0d, 0d, 0d, 0f, 0f, null);
- private boolean flying = false;
- private PlayerSettings settings;
- private List loadedChunks = new ArrayList<>();
-
- public void setLocation(EntityLocation location) {
- this.location.setXYZ(location);
- this.location.setYawPitch(location);
- }
-
- @Override
- public UUID getUUID() {
- return uuid;
- }
-
- public void setUUID(UUID uuid) {
- this.uuid = uuid;
- }
-}
diff --git a/core/src/main/java/mc/core/text/Text.java b/core/src/main/java/mc/core/text/Text.java
index 14957f7..4d470fe 100644
--- a/core/src/main/java/mc/core/text/Text.java
+++ b/core/src/main/java/mc/core/text/Text.java
@@ -1,20 +1,11 @@
-/*
- * DmitriyMX
- * 2018-06-11
- */
package mc.core.text;
import com.google.common.collect.ImmutableList;
-import lombok.EqualsAndHashCode;
import lombok.Getter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.StringJoiner;
+import java.util.*;
@Getter
-@EqualsAndHashCode
public class Text {
private static final Text EMPTY = new Text();
private static final Text NEW_LINE = new Text("\n", null, null, null);
@@ -39,7 +30,7 @@ public class Text {
}
public boolean isEmpty() {
- boolean result = content.isEmpty();
+ boolean result = (content == null || content.isEmpty());
if (children != null && !children.isEmpty()) {
for (Text child : children) {
@@ -60,6 +51,19 @@ public class Text {
}
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Text text = (Text) o;
+ return Objects.equals(toPlain(), text.toPlain());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toPlain());
+ }
+
public static class Builder {
@Getter
private String content;
@@ -81,6 +85,8 @@ public class Text {
}
public Builder(Object... objects) {
+ this.children = new ArrayList<>();
+
for(Object obj : objects) {
if (obj instanceof String) {
if (this.content == null) {
@@ -96,10 +102,10 @@ public class Text {
}
} else if (obj instanceof TextColor) {
this.color = (TextColor) obj;
+ } else if (obj instanceof Text) {
+ children.add((Text) obj);
}
}
-
- this.children = new ArrayList<>();
}
public List getChildren() {
@@ -133,8 +139,14 @@ public class Text {
return this;
}
+ public Builder append(String string) {
+ return append(Text.of(string));
+ }
+
public Builder append(Text child) {
- this.children.add(child);
+ if (child != null) {
+ this.children.add(child);
+ }
return this;
}
@@ -144,16 +156,20 @@ public class Text {
}
public Text build() {
- if (children.isEmpty()) {
+ if (children.isEmpty() && (content == null || content.isEmpty())) {
return Text.EMPTY;
}
- return new Text(
- content,
- color,
- style,
- ImmutableList.copyOf(children)
- );
+ if (children.size() == 1 && children.get(0) != null) {
+ return children.get(0);
+ } else {
+ return new Text(
+ content,
+ color,
+ style,
+ ImmutableList.copyOf(children)
+ );
+ }
}
}
diff --git a/core/src/main/java/mc/core/utils/CompactedCoords.java b/core/src/main/java/mc/core/utils/CompactedCoords.java
index 43daef0..cac3db6 100644
--- a/core/src/main/java/mc/core/utils/CompactedCoords.java
+++ b/core/src/main/java/mc/core/utils/CompactedCoords.java
@@ -25,18 +25,4 @@ public class CompactedCoords {
int i = (int)value;
return value < (double)i ? i - 1 : i;
}
-
- public static long compressXYZ(double x, double y, double z) {
- return ((floor_double(x) & 0x3FFFFFF) << 38)
- | ((floor_double(y) & 0xFFF) << 26)
- | (floor_double(z) & 0x3FFFFFF);
- }
-
- public static double[] uncompressXYZ(long compactValue) {
- return new double[]{
- compactValue >> 38,
- (compactValue >> 26) & 0x0FFF,
- compactValue << 38 >> 38 // is normal?
- };
- }
}
diff --git a/core/src/main/java/mc/core/world/World.java b/core/src/main/java/mc/core/world/World.java
index a6cbf9d..0c29708 100644
--- a/core/src/main/java/mc/core/world/World.java
+++ b/core/src/main/java/mc/core/world/World.java
@@ -5,9 +5,11 @@
package mc.core.world;
import mc.core.EntityLocation;
+import mc.core.world.block.BlockLocation;
import mc.core.world.chunk.Chunk;
public interface World {
+ String getName();
WorldType getWorldType();
EntityLocation getSpawn();
@@ -18,4 +20,12 @@ public interface World {
Chunk getChunk(int x, int z);
void setChunk(int x, int z, Chunk chunkSection);
+
+ default Chunk getChunk(BlockLocation location) {
+ return getChunk(location.getX() >> 4, location.getZ() >> 4);
+ }
+
+ default Chunk getChunk(EntityLocation location) {
+ return getChunk(location.getBlockX() >> 4, location.getBlockZ() >> 4);
+ }
}
diff --git a/core/src/main/java/mc/core/world/block/AbstractBlock.java b/core/src/main/java/mc/core/world/block/AbstractBlock.java
index 0c6d90b..b00b8af 100644
--- a/core/src/main/java/mc/core/world/block/AbstractBlock.java
+++ b/core/src/main/java/mc/core/world/block/AbstractBlock.java
@@ -3,7 +3,6 @@ package mc.core.world.block;
import com.flowpowered.nbt.Tag;
import lombok.Getter;
import lombok.Setter;
-import mc.core.Location;
import java.util.HashMap;
import java.util.Map;
@@ -12,7 +11,7 @@ import java.util.stream.Stream;
public abstract class AbstractBlock implements Block {
@Getter
@Setter
- private Location location;
+ private BlockLocation location;
@Getter
private int light = 0;
@Getter
diff --git a/core/src/main/java/mc/core/world/block/Block.java b/core/src/main/java/mc/core/world/block/Block.java
index d1a392f..b54a7d0 100644
--- a/core/src/main/java/mc/core/world/block/Block.java
+++ b/core/src/main/java/mc/core/world/block/Block.java
@@ -1,11 +1,10 @@
package mc.core.world.block;
-import mc.core.Location;
import mc.core.nbt.Taggable;
public interface Block extends Taggable{
int getLight();
void setLight(int light);
BlockType getBlockType();
- Location getLocation();
+ BlockLocation getLocation();
}
diff --git a/core/src/main/java/mc/core/world/block/BlockFactory.java b/core/src/main/java/mc/core/world/block/BlockFactory.java
index 095e312..04d039f 100644
--- a/core/src/main/java/mc/core/world/block/BlockFactory.java
+++ b/core/src/main/java/mc/core/world/block/BlockFactory.java
@@ -1,8 +1,5 @@
package mc.core.world.block;
-import mc.core.Location;
-import mc.core.world.World;
-
public class BlockFactory {
public Block create(BlockType blockType, int x, int y, int z) {
@@ -13,7 +10,7 @@ public class BlockFactory {
private class EmbeddedBlock extends AbstractBlock {
EmbeddedBlock(BlockType type, int x, int y, int z) {
super(type);
- setLocation(new Location(x, y, z));
+ setLocation(new BlockLocation(x, y, z));
}
}
}
diff --git a/core/src/main/java/mc/core/world/block/BlockLocation.java b/core/src/main/java/mc/core/world/block/BlockLocation.java
new file mode 100644
index 0000000..7c9ca85
--- /dev/null
+++ b/core/src/main/java/mc/core/world/block/BlockLocation.java
@@ -0,0 +1,30 @@
+package mc.core.world.block;
+
+import lombok.*;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Data
+public class BlockLocation implements Cloneable {
+ private int x, y, z;
+
+ public static BlockLocation ZERO() {
+ return new BlockLocation(0,0,0);
+ }
+
+ public void setXYZ(int x, int y, int z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ @Override
+ public BlockLocation clone() {
+ try {
+ return (BlockLocation) super.clone();
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/core/src/test/java/mc/core/EntityLocationTest.java b/core/src/test/java/mc/core/EntityLocationTest.java
new file mode 100644
index 0000000..052e8a6
--- /dev/null
+++ b/core/src/test/java/mc/core/EntityLocationTest.java
@@ -0,0 +1,91 @@
+package mc.core;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+class EntityLocationTest {
+ private static final ThreadLocalRandom rnd = ThreadLocalRandom.current();
+ private static final double minD = 0.0d, maxD = 10.0d;
+ private static final float minF = 0.0f, maxF = 359.9f;
+ private double x, y, z;
+ private float yaw, pitch;
+
+ @BeforeEach
+ void before() {
+ x = rnd.nextDouble(minD, maxD);
+ y = rnd.nextDouble(minD, maxD);
+ z = rnd.nextDouble(minD, maxD);
+ yaw = rnd.nextFloat() * (maxF - minF) + minF;
+ pitch = rnd.nextFloat() * (maxF - minF) + minF;
+ }
+
+ @Test
+ void equals_() {
+ EntityLocation loc1 = new EntityLocation(x, y, z, yaw, pitch);
+ EntityLocation loc2 = new EntityLocation(x, y, z, yaw, pitch);
+ assertEquals(loc1, loc2);
+
+ loc2 = new EntityLocation(x+1, y+2, z-3, yaw, pitch);
+ assertNotEquals(loc1, loc2);
+
+ loc2 = new EntityLocation(x, y, z, yaw-1, pitch+2);
+ assertNotEquals(loc1, loc2);
+ }
+
+ @Test
+ void clone_() {
+ EntityLocation locOrig = new EntityLocation(x, y, z, yaw, pitch);
+ EntityLocation locClone = locOrig.clone();
+ assertEquals(locOrig, locClone);
+ }
+
+ @Test
+ void getBlockXZ() {
+ EntityLocation location;
+
+ location = new EntityLocation(0d, 0, 0d, 0f, 0f);
+ assertEquals(0, location.getBlockX());
+ assertEquals(0, location.getBlockZ());
+
+ location.setXYZ(0.1d, 0, 0.1d);
+ assertEquals(0, location.getBlockX());
+ assertEquals(0, location.getBlockZ());
+
+ location.setXYZ(0.5d, 0, 0.5d);
+ assertEquals(0, location.getBlockX());
+ assertEquals(0, location.getBlockZ());
+
+ location.setXYZ(0.9d, 0, 0.9d);
+ assertEquals(0, location.getBlockX());
+ assertEquals(0, location.getBlockZ());
+
+ location.setXYZ(1d, 0, 1d);
+ assertEquals(1, location.getBlockX());
+ assertEquals(1, location.getBlockZ());
+
+ location.setXYZ(-0.1d, 0, -0.1d);
+ assertEquals(-1, location.getBlockX());
+ assertEquals(-1, location.getBlockZ());
+
+ location.setXYZ(-0.5d, 0, -0.5d);
+ assertEquals(-1, location.getBlockX());
+ assertEquals(-1, location.getBlockZ());
+
+ location.setXYZ(-0.9d, 0, -0.9d);
+ assertEquals(-1, location.getBlockX());
+ assertEquals(-1, location.getBlockZ());
+
+ location.setXYZ(-1d, 0, -1d);
+ assertEquals(-1, location.getBlockX());
+ assertEquals(-1, location.getBlockZ());
+
+ location.setXYZ(-1.1d, 0, -1.1d);
+ assertEquals(-2, location.getBlockX());
+ assertEquals(-2, location.getBlockZ());
+ }
+}
diff --git a/core/src/test/java/mc/core/ImmutableEntityLocationTest.java b/core/src/test/java/mc/core/ImmutableEntityLocationTest.java
new file mode 100644
index 0000000..0335538
--- /dev/null
+++ b/core/src/test/java/mc/core/ImmutableEntityLocationTest.java
@@ -0,0 +1,33 @@
+package mc.core;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class ImmutableEntityLocationTest {
+
+ @Test
+ void setValue() {
+ EntityLocation location = new ImmutableEntityLocation(1d, 2d, 3d, 4f, 5f);
+
+ assertThrows(UnsupportedOperationException.class, () -> {
+ location.setX(1);
+ location.setY(1);
+ location.setZ(1);
+ location.setYaw(1);
+ location.setPitch(1);
+ location.setXYZ(1, 2, 3);
+ location.setYawPitch(1, 2);
+ location.set(EntityLocation.ZERO());
+ });
+ }
+
+ @Test
+ void clone_() {
+ EntityLocation locOrig = new ImmutableEntityLocation(1d, 2d, 3d, 4f, 5f);
+ EntityLocation locClone = locOrig.clone();
+
+ assertEquals(locOrig, locClone);
+ }
+}
\ No newline at end of file
diff --git a/core/src/test/java/mc/core/TestEntityLocation.java b/core/src/test/java/mc/core/TestEntityLocation.java
deleted file mode 100644
index 1b8e5fb..0000000
--- a/core/src/test/java/mc/core/TestEntityLocation.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package mc.core;
-
-import mc.core.world.World;
-import mc.core.world.chunk.Chunk;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class TestEntityLocation {
- private World world;
-
- @Before
- public void prepareWorld() {
- this.world = mock(World.class);
- when(world.getChunk(anyInt(), anyInt())).thenAnswer(invocation -> {
- Object[] args = invocation.getArguments();
-
- Chunk chunk = mock(Chunk.class);
- when(chunk.getX()).thenReturn((int) args[0]);
- when(chunk.getZ()).thenReturn((int) args[1]);
-
- return chunk;
- });
- }
-
- @Test
- public void cloneTest() {
- EntityLocation firstLocation = new EntityLocation(10, 20, 30, 40, 50, world);
- assertSame("Lost world reference before cloning", world, firstLocation.getWorld());
- EntityLocation locationClone = firstLocation.clone();
-
- assertEquals("X mismatch", firstLocation.getX(), locationClone.getX(), 0);
- assertEquals("Y mismatch", firstLocation.getY(), locationClone.getY(), 0);
- assertEquals("Z mismatch", firstLocation.getZ(), locationClone.getZ(), 0);
- assertEquals("Pitch mismatch", firstLocation.getPitch(), locationClone.getPitch(), 0);
- assertEquals("Yaw mismatch", firstLocation.getYaw(), locationClone.getYaw(), 0);
- assertSame("World mismatch (accidental clone of the World object?)", firstLocation.getWorld(), locationClone.getWorld());
- }
-
- @Test
- public void testGetChunk() {
- EntityLocation location;
- Chunk chunk;
-
- location = new EntityLocation(0d, 0, 0d, 0f, 0f, world);
- chunk = location.getChunk();
- assertEquals(0, chunk.getX());
- assertEquals(0, chunk.getZ());
-
- location.setXYZ(1d, 0, 1d);
- chunk = location.getChunk();
- assertEquals(0, chunk.getX());
- assertEquals(0, chunk.getZ());
-
- location.setXYZ(15d, 0, 15d);
- chunk = location.getChunk();
- assertEquals(0, chunk.getX());
- assertEquals(0, chunk.getZ());
-
- location.setXYZ(16d, 0, 16d);
- chunk = location.getChunk();
- assertEquals(1, chunk.getX());
- assertEquals(1, chunk.getZ());
-
- location.setXYZ(-0.1d, 0, -0.1d);
- chunk = location.getChunk();
- assertEquals(-1, chunk.getX());
- assertEquals(-1, chunk.getZ());
-
- location.setXYZ(-1d, 0, -1d);
- chunk = location.getChunk();
- assertEquals(-1, chunk.getX());
- assertEquals(-1, chunk.getZ());
-
- location.setXYZ(-15d, 0, -15d);
- chunk = location.getChunk();
- assertEquals(-1, chunk.getX());
- assertEquals(-1, chunk.getZ());
-
- //TODO на практике, таких точных значений не встретиться, но тем не менее данный тест не проходит
- //location.setXYZ(-16.0d, 0, -16.0d);
- //chunk = location.getChunk();
- //assertEquals(-2, chunk.getX());
- //assertEquals(-2, chunk.getZ());
-
- location.setXYZ(-16.001d, 0, -16.001d);
- chunk = location.getChunk();
- assertEquals(-2, chunk.getX());
- assertEquals(-2, chunk.getZ());
- }
-}
diff --git a/core/src/test/java/mc/core/TestLocation.java b/core/src/test/java/mc/core/TestLocation.java
deleted file mode 100644
index 4faef07..0000000
--- a/core/src/test/java/mc/core/TestLocation.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package mc.core;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class TestLocation {
- @Test
- public void testGetBlockXZ() {
- Location location;
-
- location = new Location(0d, 0, 0d);
- assertEquals(0, location.getBlockX());
- assertEquals(0, location.getBlockZ());
-
- location.setXYZ(0.1d, 0, 0.1d);
- assertEquals(0, location.getBlockX());
- assertEquals(0, location.getBlockZ());
-
- location.setXYZ(0.5d, 0, 0.5d);
- assertEquals(0, location.getBlockX());
- assertEquals(0, location.getBlockZ());
-
- location.setXYZ(0.9d, 0, 0.9d);
- assertEquals(0, location.getBlockX());
- assertEquals(0, location.getBlockZ());
-
- location.setXYZ(1d, 0, 1d);
- assertEquals(1, location.getBlockX());
- assertEquals(1, location.getBlockZ());
-
- location.setXYZ(-0.1d, 0, -0.1d);
- assertEquals(-1, location.getBlockX());
- assertEquals(-1, location.getBlockZ());
-
- location.setXYZ(-0.5d, 0, -0.5d);
- assertEquals(-1, location.getBlockX());
- assertEquals(-1, location.getBlockZ());
-
- location.setXYZ(-0.9d, 0, -0.9d);
- assertEquals(-1, location.getBlockX());
- assertEquals(-1, location.getBlockZ());
-
- location.setXYZ(-1d, 0, -1d);
- assertEquals(-1, location.getBlockX());
- assertEquals(-1, location.getBlockZ());
-
- location.setXYZ(-1.1d, 0, -1.1d);
- assertEquals(-2, location.getBlockX());
- assertEquals(-2, location.getBlockZ());
- }
-}
diff --git a/core/src/test/java/mc/core/TestSpringConfig.java b/core/src/test/java/mc/core/TestSpringConfig.java
new file mode 100644
index 0000000..34dc1de
--- /dev/null
+++ b/core/src/test/java/mc/core/TestSpringConfig.java
@@ -0,0 +1,34 @@
+package mc.core;
+
+import mc.core.world.World;
+import mc.core.world.chunk.Chunk;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@Configuration
+public class TestSpringConfig {
+ @Bean()
+ public World simpleMockWorld() {
+ return mock(World.class);
+ }
+
+ @Bean
+ public World chunkedMockWorld() {
+ World world = mock(World.class);
+ when(world.getChunk(anyInt(), anyInt())).thenAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+
+ Chunk chunk = mock(Chunk.class);
+ when(chunk.getX()).thenReturn((int) args[0]);
+ when(chunk.getZ()).thenReturn((int) args[1]);
+
+ return chunk;
+ });
+
+ return world;
+ }
+}
diff --git a/core/src/test/java/mc/core/text/TextTest.java b/core/src/test/java/mc/core/text/TextTest.java
new file mode 100644
index 0000000..fbb417b
--- /dev/null
+++ b/core/src/test/java/mc/core/text/TextTest.java
@@ -0,0 +1,76 @@
+package mc.core.text;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class TextTest {
+ @Test
+ void toPlain() {
+ final String m1 = "mes";
+ final String m2 = "sage";
+ final String message = m1 + m2;
+
+ assertEquals(message, Text.of(message).toPlain());
+ assertEquals(message, Text.builder(message).build().toPlain());
+ assertEquals(message, Text.builder(Text.of(message)).build().toPlain());
+ assertEquals(message, Text.builder().append(message).build().toPlain());
+ assertEquals(message, Text.builder().append(Text.of(message)).build().toPlain());
+
+ assertEquals(message, Text.builder(m1, m2).build().toPlain());
+ assertEquals(message, Text.builder(Text.of(m1), Text.of(m2)).build().toPlain());
+ assertEquals(message, Text.builder().append(Text.of(m1), Text.of(m2)).build().toPlain());
+ assertEquals(message, Text.builder().append(Text.of(m1)).append(Text.of(m2)).build().toPlain());
+
+
+ }
+
+ @Test
+ void equals_() {
+ assertEquals(Text.of(), Text.of(""));
+ assertEquals(Text.of(), Text.builder().build());
+ assertEquals(Text.of(), Text.builder("").build());
+ assertEquals(Text.of(), Text.builder().append().build());
+ assertEquals(Text.of(), Text.builder().append("").build());
+
+ assertNotEquals(Text.of(), Text.of("??"));
+ assertNotEquals(Text.of(), Text.builder("??").build());
+ assertNotEquals(Text.of(), Text.builder().append("??").build());
+
+ assertEquals(Text.of("message"), Text.builder("message").build());
+ assertEquals(Text.of("message"), Text.builder(Text.of("message")).build());
+ assertEquals(Text.of("message"), Text.builder().append("message").build());
+ assertEquals(Text.of("message"), Text.builder().append(Text.of("message")).build());
+ }
+
+ @Test
+ void isEmpty() {
+ assertTrue(Text.of().isEmpty());
+ assertTrue(Text.of((String) null).isEmpty());
+ assertTrue(Text.of((Text) null).isEmpty());
+ assertTrue(Text.of("").isEmpty());
+ assertTrue(Text.of("", "").isEmpty());
+
+ assertTrue(Text.builder().build().isEmpty());
+ assertTrue(Text.builder((String) null).build().isEmpty());
+ assertTrue(Text.builder((Text) null).build().isEmpty());
+ assertTrue(Text.builder("").build().isEmpty());
+ assertTrue(Text.builder("", "").build().isEmpty());
+ assertTrue(Text.builder(Text.of()).build().isEmpty());
+ assertTrue(Text.builder(Text.of(), Text.of()).build().isEmpty());
+
+ assertTrue(Text.builder().append().build().isEmpty());
+ assertTrue(Text.builder().append((String) null).build().isEmpty());
+ assertTrue(Text.builder().append((Text) null).build().isEmpty());
+ assertTrue(Text.builder().append("").build().isEmpty());
+ assertTrue(Text.builder().append(Text.of()).build().isEmpty());
+ assertTrue(Text.builder().append(Text.of(), Text.of()).build().isEmpty());
+ assertTrue(Text.builder().append(Text.of()).append(Text.of()).build().isEmpty());
+
+ assertFalse(Text.of("??").isEmpty());
+ assertFalse(Text.builder("??").build().isEmpty());
+ assertFalse(Text.builder(Text.of("??")).build().isEmpty());
+ assertFalse(Text.builder().append("??").build().isEmpty());
+ assertFalse(Text.builder().append(Text.of("??")).build().isEmpty());
+ }
+}
diff --git a/core/src/test/java/mc/core/utils/CompactedCoordsTest.java b/core/src/test/java/mc/core/utils/CompactedCoordsTest.java
new file mode 100644
index 0000000..f88443e
--- /dev/null
+++ b/core/src/test/java/mc/core/utils/CompactedCoordsTest.java
@@ -0,0 +1,25 @@
+package mc.core.utils;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class CompactedCoordsTest {
+ @Test
+ void compressXZ() {
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+
+ for (int i = 0; i < 100; i++) {
+ final int x = random.nextInt(Short.MIN_VALUE, Short.MAX_VALUE);
+ final int z = random.nextInt(Short.MIN_VALUE, Short.MAX_VALUE);
+
+ final int compressXZ = CompactedCoords.compressXZ(x, z);
+ int[] xz = CompactedCoords.uncompressXZ(compressXZ);
+
+ assertEquals(x, xz[0]);
+ assertEquals(z, xz[1]);
+ }
+ }
+}
diff --git a/core/src/test/java/mc/core/utils/TestCompactedCoords.java b/core/src/test/java/mc/core/utils/TestCompactedCoords.java
deleted file mode 100644
index 0aef3da..0000000
--- a/core/src/test/java/mc/core/utils/TestCompactedCoords.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package mc.core.utils;
-
-import org.junit.Test;
-
-import java.util.Random;
-
-import static org.junit.Assert.assertEquals;
-
-
-public class TestCompactedCoords {
- @Test
- public void testXZSimple() {
- for (int z = -100; z <= 100; z++) {
- for (int x = -100; x <= 100; x++) {
- int compressXZ = CompactedCoords.compressXZ(x, z);
- int[] xz = CompactedCoords.uncompressXZ(compressXZ);
-
- assertEquals(x, xz[0]);
- assertEquals(z, xz[1]);
- }
- }
- }
-
- @Test
- public void testXZRandom() {
- Random random = new Random();
- int x,z;
-
- for (int i = 0; i < 100; i++) {
- do {
- x = random.nextInt();
- } while (x < Short.MIN_VALUE || x > Short.MAX_VALUE);
-
- do {
- z = random.nextInt();
- } while (z < Short.MIN_VALUE || z > Short.MAX_VALUE);
-
-
- int compressXZ = CompactedCoords.compressXZ(x, z);
- int[] xz = CompactedCoords.uncompressXZ(compressXZ);
-
- assertEquals(x, xz[0]);
- assertEquals(z, xz[1]);
- }
- }
-
-// @Test
- public void testXYZSimple() {
- for (int z = -100; z <= 100; z++) {
- for (int x = -100; x <= 100; x++) {
- for (int y = -100; y <= 100; y++) {
- long compressXYZ = CompactedCoords.compressXYZ(x, y, z);
- double[] xyz = CompactedCoords.uncompressXYZ(compressXYZ);
-
- assertEquals(x, xyz[0], 0.001d);
- assertEquals(y, xyz[1], 0.001d);
- assertEquals(z, xyz[2], 0.001d);
- }
- }
- }
- }
-}
diff --git a/core/src/test/java/mc/core/world/block/BlockLocationTest.java b/core/src/test/java/mc/core/world/block/BlockLocationTest.java
new file mode 100644
index 0000000..d716995
--- /dev/null
+++ b/core/src/test/java/mc/core/world/block/BlockLocationTest.java
@@ -0,0 +1,39 @@
+package mc.core.world.block;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+class BlockLocationTest {
+ private static final ThreadLocalRandom rnd = ThreadLocalRandom.current();
+ private static final int minI = 0, maxI = 10;
+ private int x, y, z;
+
+ @BeforeEach
+ void before() {
+ x = rnd.nextInt(minI, maxI);
+ y = rnd.nextInt(minI, maxI);
+ z = rnd.nextInt(minI, maxI);
+ }
+
+ @Test
+ void equals_() {
+ BlockLocation loc1 = new BlockLocation(x, y, z);
+ BlockLocation loc2 = new BlockLocation(x, y, z);
+ assertEquals(loc1, loc2);
+
+ loc2 = new BlockLocation(x+1, y+2, z-3);
+ assertNotEquals(loc1, loc2);
+ }
+
+ @Test
+ void clone_() {
+ BlockLocation locOrig = new BlockLocation(x, y, z);
+ BlockLocation locClone = locOrig.clone();
+ assertEquals(locOrig, locClone);
+ }
+}
diff --git a/h2_playermanager/build.gradle b/h2_playermanager/build.gradle
new file mode 100644
index 0000000..acdbf0c
--- /dev/null
+++ b/h2_playermanager/build.gradle
@@ -0,0 +1,17 @@
+version '1.0-SNAPSHOT'
+
+ext {
+ spring_data_version = '2.1.0.RELEASE'
+}
+
+dependencies {
+ /* Core */
+ compile_excludeCopy project(':core')
+
+ /* Spring */
+ compile (group: 'org.springframework.data', name: 'spring-data-jpa', version: spring_data_version)
+
+ /* Database */
+ compile (group: 'org.hibernate', name: 'hibernate-entitymanager', version: '5.3.6.Final')
+ compile (group: 'com.h2database', name: 'h2', version: '1.4.197')
+}
\ No newline at end of file
diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java b/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java
new file mode 100644
index 0000000..8880503
--- /dev/null
+++ b/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java
@@ -0,0 +1,63 @@
+package mc.core.h2db;
+
+import lombok.Data;
+import mc.core.EntityLocation;
+import mc.core.exception.ResourceUnloadedException;
+import mc.core.network.NetChannel;
+import mc.core.player.Player;
+import mc.core.player.PlayerSettings;
+import mc.core.world.World;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+@Data
+public class H2Player implements Player {
+ private int id;
+ private UUID uuid;
+ private String name;
+ private boolean online = false;
+ private List loadedChunks;
+ private NetChannel channel;
+ private EntityLocation location;
+ private Reference $refWorld;
+ private boolean flying = false;
+ private PlayerSettings settings;
+
+ @Override
+ public World getWorld() {
+ if ($refWorld == null) {
+ return null;
+ } else if ($refWorld.get() == null) {
+ throw new ResourceUnloadedException("You're trying to get unloaded world");
+ } else {
+ return $refWorld.get();
+ }
+ }
+
+ @Override
+ public void setWorld(World world) {
+ if (world == null) {
+ this.$refWorld = null;
+ } else {
+ this.$refWorld = new WeakReference<>(world);
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ H2Player player = (H2Player) obj;
+ return id == player.id &&
+ Objects.equals(uuid, player.uuid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, uuid);
+ }
+}
diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerManager.java b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerManager.java
new file mode 100644
index 0000000..9741d30
--- /dev/null
+++ b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerManager.java
@@ -0,0 +1,115 @@
+package mc.core.h2db;
+
+import com.google.common.collect.ImmutableList;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import mc.core.EntityLocation;
+import mc.core.h2db.service.H2PlayerService;
+import mc.core.network.BroadcastNetChannel;
+import mc.core.network.NetChannel;
+import mc.core.player.Player;
+import mc.core.player.PlayerManager;
+import mc.core.player.PlayerSettings;
+import mc.core.world.World;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+@Slf4j
+@Component
+public class H2PlayerManager implements PlayerManager {
+ @Setter
+ @Autowired
+ private H2PlayerService h2PlayerService;
+ private List playerList = Collections.synchronizedList(new ArrayList<>());
+ @Autowired
+ private World world; //FIXME
+
+ @Override
+ public Player createPlayer(String name, EntityLocation location, World world) {
+ H2Player h2Player = new H2Player();
+ h2Player.setName(name);
+ h2Player.setUuid(UUID.randomUUID());
+ h2Player.setLocation(location.clone());
+ h2Player.setLoadedChunks(new ArrayList<>());
+ h2Player.setWorld(world);
+ h2Player.setSettings(new PlayerSettings());
+
+ return h2PlayerService.save(h2Player);
+ }
+
+ @Override
+ public void joinServer(Player player) {
+ //TODO в дальнейшем следует именно этому методу передать функции инсерта в БД
+ H2Player h2Player = (H2Player) player;
+ playerList.add(h2Player);
+ h2Player.setOnline(true);
+ }
+
+ @Override
+ public void leftServer(Player player) {
+ H2Player h2Player = (H2Player) player;
+ h2PlayerService.save(h2Player);
+ h2Player.setOnline(false);
+ h2Player.getLoadedChunks().clear();
+ }
+
+ @Override
+ public Player getPlayer(String name) {
+ return playerList.stream()
+ .filter(player -> player.getName().equals(name))
+ .filter(H2Player::isOnline)
+ .findFirst().orElse(null);
+ }
+
+ @Override
+ public List getPlayers() {
+ return playerList.stream()
+ .filter(H2Player::isOnline)
+ .collect(ImmutableList.toImmutableList());
+ }
+
+ @Override
+ public int getCountPlayers() {
+ return (int) playerList.stream()
+ .filter(H2Player::isOnline)
+ .count();
+ }
+
+ @Override
+ public NetChannel getBroadcastChannel() {
+ return new BroadcastNetChannel(
+ playerList.stream()
+ .filter(H2Player::isOnline)
+ .map(player -> (Player)player)
+ );
+ }
+
+ @Override
+ public Player getOfflinePlayer(String name) {
+ //TODO похоже в попытке где-то оптимизировать/сэконопить я сам себя ******[обманул]
+ //необходимо этот участок кода переписать
+ //потому как похоже на экономию на спичках
+ H2Player h2Player = playerList.stream()
+ .filter(player -> player.getName().equals(name))
+ .filter(player -> !player.isOnline())
+ .findFirst().orElse(null);
+
+ if (h2Player == null) {
+ h2Player = h2PlayerService.getByName(name);
+ if (h2Player != null) {
+ h2Player.setWorld(world);
+ return h2Player;
+ } else {
+ return null;
+ }
+ } else {
+ h2Player.setWorld(world);
+ return h2Player;
+ }
+ }
+}
diff --git a/h2_playermanager/src/main/java/mc/core/h2db/entity/H2PlayerEntity.java b/h2_playermanager/src/main/java/mc/core/h2db/entity/H2PlayerEntity.java
new file mode 100644
index 0000000..1edc5f4
--- /dev/null
+++ b/h2_playermanager/src/main/java/mc/core/h2db/entity/H2PlayerEntity.java
@@ -0,0 +1,104 @@
+package mc.core.h2db.entity;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import mc.core.EntityLocation;
+import mc.core.h2db.H2Player;
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.*;
+import java.util.UUID;
+
+@Entity
+@Table(name = "players",
+ indexes = {@Index(name = "idx_players_uuid", columnList = "uuid", unique = true),
+ @Index(name = "idx_players_name", columnList = "name")})
+@NoArgsConstructor
+@Data
+public class H2PlayerEntity {
+ @Id
+ @GeneratedValue(generator = "increment")
+ @GenericGenerator(name= "increment", strategy= "increment")
+ @Column(nullable = false)
+ private Long id;
+
+ @Column(length = 36, nullable = false)
+ private String uuid;
+
+ @Column(length = 16, nullable = false)
+ private String name;
+
+ @Column(name = "location_x", nullable = false)
+ private Double locationX;
+
+ @Column(name = "location_y", nullable = false)
+ private Double locationY;
+
+ @Column(name = "location_z", nullable = false)
+ private Double locationZ;
+
+ @Column(name = "location_yaw", nullable = false)
+ private Float locationYaw;
+
+ @Column(name = "location_pitch", nullable = false)
+ private Float locationPitch;
+
+ @Column(name = "location_world", length = 64, nullable = false)
+ private String locationWorld;
+
+ public H2PlayerEntity(H2Player player) {
+ this.id = (long) player.getId();
+ setUuid(player.getUuid().toString());
+ setName(this.name = player.getName());
+ this.locationX = player.getLocation().getX();
+ this.locationY = player.getLocation().getY();
+ this.locationZ = player.getLocation().getZ();
+ this.locationYaw = player.getLocation().getYaw();
+ this.locationPitch = player.getLocation().getPitch();
+ if (player.getWorld() != null) { //FIXME
+ this.locationWorld = player.getWorld().getName();
+ } else {
+ this.locationWorld = "null_world";
+ }
+ }
+
+ public void setUuid(String uuid) {
+ if (uuid == null || uuid.trim().isEmpty()) {
+ this.uuid = null;
+ } else {
+ this.uuid = uuid;
+ }
+ }
+
+ public void setName(String name) {
+ if (name == null || name.trim().isEmpty()) {
+ this.name = null;
+ } else {
+ this.name = name;
+ }
+ }
+
+ public H2Player toPlayer() {
+ H2Player player = new H2Player();
+ return toPlayer(player);
+ }
+
+ public H2Player toPlayer(H2Player player) {
+ player.setId(this.id.intValue());
+ player.setUuid(UUID.fromString(this.uuid));
+ player.setName(this.name);
+ if (player.getLocation() == null) {
+ player.setLocation(new EntityLocation(
+ this.locationX, this.locationY, this.locationZ,
+ this.locationYaw, this.locationPitch
+ ));
+ } else {
+ player.getLocation().setXYZ(this.locationX, this.locationY, this.locationZ);
+ player.getLocation().setYawPitch(this.locationYaw, this.locationPitch);
+ }
+
+ player.setWorld(null); //FIXME
+
+ return player;
+ }
+}
diff --git a/h2_playermanager/src/main/java/mc/core/h2db/repository/H2PlayerEntityRepository.java b/h2_playermanager/src/main/java/mc/core/h2db/repository/H2PlayerEntityRepository.java
new file mode 100644
index 0000000..47b4f45
--- /dev/null
+++ b/h2_playermanager/src/main/java/mc/core/h2db/repository/H2PlayerEntityRepository.java
@@ -0,0 +1,12 @@
+package mc.core.h2db.repository;
+
+import mc.core.h2db.entity.H2PlayerEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+public interface H2PlayerEntityRepository extends JpaRepository {
+ Optional findByName(String name);
+}
diff --git a/h2_playermanager/src/main/java/mc/core/h2db/service/H2PlayerService.java b/h2_playermanager/src/main/java/mc/core/h2db/service/H2PlayerService.java
new file mode 100644
index 0000000..a20fb7b
--- /dev/null
+++ b/h2_playermanager/src/main/java/mc/core/h2db/service/H2PlayerService.java
@@ -0,0 +1,11 @@
+package mc.core.h2db.service;
+
+import mc.core.h2db.H2Player;
+
+public interface H2PlayerService {
+ H2Player save(H2Player player);
+ void remove(H2Player player);
+
+ H2Player getByName(String name);
+ H2Player getById(int id);
+}
diff --git a/h2_playermanager/src/main/java/mc/core/h2db/service/H2PlayerServiceImpl.java b/h2_playermanager/src/main/java/mc/core/h2db/service/H2PlayerServiceImpl.java
new file mode 100644
index 0000000..d716444
--- /dev/null
+++ b/h2_playermanager/src/main/java/mc/core/h2db/service/H2PlayerServiceImpl.java
@@ -0,0 +1,41 @@
+package mc.core.h2db.service;
+
+import mc.core.h2db.H2Player;
+import mc.core.h2db.entity.H2PlayerEntity;
+import mc.core.h2db.repository.H2PlayerEntityRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+
+@Service
+public class H2PlayerServiceImpl implements H2PlayerService {
+ @Autowired
+ private H2PlayerEntityRepository h2PlayerEntityRepository;
+
+ @Override
+ public H2Player save(H2Player player) {
+ H2PlayerEntity entity = new H2PlayerEntity(player);
+ //TODO возможно имеет смысл здесь оптимизация
+ //вместо toPlayer() сделать toPlayer(H2Player) который в существующий
+ //будет дописывать/обновлять данные
+ return h2PlayerEntityRepository.saveAndFlush(entity).toPlayer(player);
+ }
+
+ @Override
+ public void remove(H2Player player) {
+ h2PlayerEntityRepository.deleteById((long) player.getId());
+ }
+
+ @Override
+ public H2Player getByName(String name) {
+ Optional optEntity = h2PlayerEntityRepository.findByName(name);
+ return optEntity.map(H2PlayerEntity::toPlayer).orElse(null);
+ }
+
+ @Override
+ public H2Player getById(int id) {
+ Optional optEntity = h2PlayerEntityRepository.findById((long) id);
+ return optEntity.map(H2PlayerEntity::toPlayer).orElse(null);
+ }
+}
diff --git a/h2_playermanager/src/test/java/mc/core/h2db/H2PlayerManagerTest.java b/h2_playermanager/src/test/java/mc/core/h2db/H2PlayerManagerTest.java
new file mode 100644
index 0000000..dd25625
--- /dev/null
+++ b/h2_playermanager/src/test/java/mc/core/h2db/H2PlayerManagerTest.java
@@ -0,0 +1,159 @@
+package mc.core.h2db;
+
+import mc.core.EntityLocation;
+import mc.core.h2db.service.H2PlayerService;
+import mc.core.player.Player;
+import mc.core.world.World;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(classes = {TestSpringConfig.class})
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+class H2PlayerManagerTest {
+ @Autowired
+ private H2PlayerService h2PlayerService;
+ @Autowired
+ private World mockWorld;
+ @Autowired
+ private H2PlayerManager playerManager;
+
+ @Test
+ void createPlayer() {
+ final String playerName = "NEW_PLAYER";
+ final Player newPlayer = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
+
+ assertNotNull(newPlayer);
+ assertEquals(H2Player.class, newPlayer.getClass());
+ assertTrue(newPlayer.getId() > 0);
+
+ final H2Player queryPlayer = h2PlayerService.getByName(playerName);
+ assertTrue(queryPlayer.getId() > 0);
+
+ assertEquals(newPlayer, queryPlayer);
+ assertEquals(newPlayer.getName(), queryPlayer.getName());
+ assertEquals(newPlayer.getLocation(), queryPlayer.getLocation());
+ assertEquals(newPlayer.getWorld(), queryPlayer.getWorld());
+ }
+
+ @Test
+ void joinServer() {
+ assertEquals(0, playerManager.getCountPlayers());
+
+ final String playerName = "NEW_PLAYER";
+ final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
+ playerManager.joinServer(player);
+
+ assertEquals(1, playerManager.getCountPlayers());
+ assertTrue(player.isOnline());
+ }
+
+ @Test
+ void leftServer() {
+ assertEquals(0, playerManager.getCountPlayers());
+
+ final String playerName = "NEW_PLAYER";
+ final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
+ playerManager.joinServer(player);
+
+ assertEquals(1, playerManager.getCountPlayers());
+ assertTrue(player.isOnline());
+
+ final int playerId = player.getId();
+
+ final String anotherName = "ANOTHER_NAME";
+ ((H2Player)player).setName(anotherName);
+
+ playerManager.leftServer(player);
+
+ assertEquals(0, playerManager.getCountPlayers());
+
+ assertFalse(player.isOnline());
+ assertTrue(player.getLoadedChunks().isEmpty());
+
+ final H2Player queryPlayer = h2PlayerService.getById(playerId);
+
+ assertNotNull(queryPlayer);
+ ((H2Player)player).setId(playerId);
+ assertEquals(player, queryPlayer);
+ }
+
+ @Test
+ void getPlayer() {
+ assertEquals(0, playerManager.getCountPlayers());
+
+ final String playerName = "NEW_PLAYER";
+ final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
+ assertNotNull(player);
+
+ playerManager.joinServer(player);
+
+ assertEquals(1, playerManager.getCountPlayers());
+
+ Player queryPlayer = playerManager.getPlayer(playerName);
+
+ assertEquals(player, queryPlayer);
+ }
+
+ @Test
+ void getPlayers() {
+ assertEquals(0, playerManager.getCountPlayers());
+
+ final String playerName = "NEW_PLAYER";
+ final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
+ assertNotNull(player);
+
+ playerManager.joinServer(player);
+
+ assertEquals(1, playerManager.getCountPlayers());
+
+ List players = playerManager.getPlayers();
+ assertEquals(1, players.size());
+ try {
+ players.add(new H2Player());
+ fail();
+ } catch (Exception e) {
+ assertTrue(true);
+ }
+
+ assertEquals(player, players.get(0));
+ }
+
+ @Test
+ void getCountPlayers() {
+ assertEquals(0, playerManager.getCountPlayers());
+
+ final String playerName = "NEW_PLAYER";
+ final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
+ assertNotNull(player);
+
+ playerManager.joinServer(player);
+
+ assertEquals(1, playerManager.getCountPlayers());
+
+ playerManager.leftServer(player);
+
+ assertEquals(0, playerManager.getCountPlayers());
+ }
+
+ @Test
+ void getOfflinePlayer() {
+ final String playerName = "NEW_PLAYER";
+ final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
+ playerManager.joinServer(player);
+ playerManager.leftServer(player);
+
+ assertEquals(0, playerManager.getCountPlayers());
+
+ Player offlinePlayer = playerManager.getOfflinePlayer(playerName);
+ assertEquals(player, offlinePlayer);
+ }
+}
diff --git a/h2_playermanager/src/test/java/mc/core/h2db/H2PlayerTest.java b/h2_playermanager/src/test/java/mc/core/h2db/H2PlayerTest.java
new file mode 100644
index 0000000..271fae7
--- /dev/null
+++ b/h2_playermanager/src/test/java/mc/core/h2db/H2PlayerTest.java
@@ -0,0 +1,36 @@
+package mc.core.h2db;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+class H2PlayerTest {
+ @Test
+ void equals_() {
+ UUID uuid = UUID.randomUUID();
+
+ H2Player player1 = new H2Player();
+ player1.setId(1);
+ player1.setUuid(uuid);
+ player1.setName("Player1");
+
+ H2Player player2 = new H2Player();
+ player2.setId(1);
+ player2.setUuid(uuid);
+ player2.setName("Player2");
+
+ assertEquals(player1, player2);
+
+ player2.setId(2);
+
+ assertNotEquals(player1, player2);
+
+ player2.setId(1);
+ player2.setUuid(UUID.randomUUID());
+
+ assertNotEquals(player1, player2);
+ }
+}
diff --git a/h2_playermanager/src/test/java/mc/core/h2db/TestSpringConfig.java b/h2_playermanager/src/test/java/mc/core/h2db/TestSpringConfig.java
new file mode 100644
index 0000000..5ff647b
--- /dev/null
+++ b/h2_playermanager/src/test/java/mc/core/h2db/TestSpringConfig.java
@@ -0,0 +1,90 @@
+package mc.core.h2db;
+
+import mc.core.h2db.service.H2PlayerService;
+import mc.core.world.World;
+import org.hibernate.jpa.HibernatePersistenceProvider;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.persistence.EntityManagerFactory;
+import javax.sql.DataSource;
+import java.util.Properties;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@Configuration
+@EnableJpaRepositories
+@EnableTransactionManagement
+@ComponentScan("mc.core.h2db")
+public class TestSpringConfig {
+ private static final String DATABASE_DRIVER = "org.h2.Driver";
+ private static final String DATABASE_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";
+ private static final String DATABASE_USERNAME = "sa";
+ private static final String DATABASE_PASSWORD = "s3cReT";
+
+ static {
+ System.setProperty("org.jboss.logging.provider", "slf4j");
+ }
+
+ private Properties hibernateProp() {
+ Properties properties = new Properties();
+ properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
+ properties.put("hibernate.show_sql", "true");
+ properties.put("hibernate.format_sql", "true");
+ properties.put("hibernate.use_sql_comments", "true");
+ properties.put("hibernate.hbm2ddl.auto", "create");
+
+ return properties;
+ }
+
+ @Bean
+ public World mockWorld() {
+ World mockWorld = mock(World.class);
+ when(mockWorld.getName()).thenReturn("mock_world");
+ return mockWorld;
+ }
+
+ @Bean
+ public DataSource dataSource() {
+ DriverManagerDataSource dmds = new DriverManagerDataSource();
+ dmds.setDriverClassName(DATABASE_DRIVER);
+ dmds.setUrl(DATABASE_URL);
+ dmds.setUsername(DATABASE_USERNAME);
+ dmds.setPassword(DATABASE_PASSWORD);
+
+ return dmds;
+ }
+
+ @Bean
+ public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
+ LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
+ entityManagerFactoryBean.setDataSource(dataSource);
+ entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
+ entityManagerFactoryBean.setPackagesToScan("mc.core.h2db.entity");
+ entityManagerFactoryBean.setJpaProperties(hibernateProp());
+
+ return entityManagerFactoryBean;
+ }
+
+ @Bean
+ public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
+ JpaTransactionManager transactionManager = new JpaTransactionManager();
+ transactionManager.setEntityManagerFactory(entityManagerFactory);
+
+ return transactionManager;
+ }
+
+ @Bean
+ public H2PlayerManager h2PlayerManager(H2PlayerService h2PlayerService) {
+ H2PlayerManager playerManager = new H2PlayerManager();
+ playerManager.setH2PlayerService(h2PlayerService);
+ return playerManager;
+ }
+}
diff --git a/h2_playermanager/src/test/java/mc/core/h2db/service/H2PlayerServiceTest.java b/h2_playermanager/src/test/java/mc/core/h2db/service/H2PlayerServiceTest.java
new file mode 100644
index 0000000..6440b3e
--- /dev/null
+++ b/h2_playermanager/src/test/java/mc/core/h2db/service/H2PlayerServiceTest.java
@@ -0,0 +1,144 @@
+package mc.core.h2db.service;
+
+import mc.core.EntityLocation;
+import mc.core.h2db.H2Player;
+import mc.core.h2db.TestSpringConfig;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(classes = {TestSpringConfig.class})
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+class H2PlayerServiceTest {
+ @Autowired
+ private H2PlayerService h2PlayerService;
+
+ private H2Player buildPlayer() {
+ final ThreadLocalRandom rnd = ThreadLocalRandom.current();
+ final double minD = 0.0d, maxD = 10.0d;
+ final float minF = 0.0f, maxF = 359.9f;
+ final int minI = 1000, maxI = 9999;
+
+ final H2Player player = new H2Player();
+ player.setUuid(UUID.randomUUID());
+ player.setName("player" + rnd.nextInt(minI, maxI));
+ player.setLocation(new EntityLocation(
+ rnd.nextDouble(minD, maxD),
+ rnd.nextDouble(minD, maxD),
+ rnd.nextDouble(minD, maxD),
+ rnd.nextFloat() * (maxF - minF) + minF,
+ rnd.nextFloat() * (maxF - minF) + minF
+ ));
+ player.setWorld(null); //FIXME
+
+ return player;
+ }
+
+ @Test
+ void save() {
+ H2Player player = buildPlayer();
+ H2Player savedPlayer = h2PlayerService.save(player);
+
+ player.setId(savedPlayer.getId()); //FIXME костыль, однако
+ assertEquals(player, savedPlayer);
+ }
+
+ @Test
+ void save_NameEmpty() {
+ assertThrows(Exception.class, () -> {
+ H2Player player = buildPlayer();
+ player.setName("");
+ h2PlayerService.save(player);
+ });
+ }
+
+ @Test
+ void save_NameNull() {
+ assertThrows(Exception.class, () -> {
+ H2Player player = buildPlayer();
+ player.setName(null);
+ h2PlayerService.save(player);
+ });
+ }
+
+ @Test
+ void save_UuidNull() {
+ assertThrows(Exception.class, () -> {
+ H2Player player = buildPlayer();
+ player.setUuid(null);
+ h2PlayerService.save(player);
+ });
+ }
+
+ @Test
+ void save_LocationNull() {
+ assertThrows(Exception.class, () -> {
+ H2Player player = buildPlayer();
+ player.setLocation(null);
+ h2PlayerService.save(player);
+ });
+ }
+
+ @Test
+ void remove() {
+ H2Player player = h2PlayerService.save(buildPlayer());
+ h2PlayerService.remove(player);
+
+ H2Player player2 = h2PlayerService.getById(player.getId());
+ assertNull(player2);
+ }
+
+ @Test
+ void remove_NotExists() {
+ assertThrows(Exception.class, () -> {
+ H2Player player = h2PlayerService.save(buildPlayer());
+ h2PlayerService.remove(player);
+ h2PlayerService.remove(player);
+ });
+ }
+
+ @Test
+ void getByName() {
+ H2Player player = h2PlayerService.save(buildPlayer());
+
+ H2Player player2 = h2PlayerService.getByName(player.getName());
+ assertEquals(player, player2);
+ }
+
+ @Test
+ void getByName_NotExists() {
+ assertNull(h2PlayerService.getByName("UNKNOW_PLAYER"));
+ }
+
+ @Test
+ void getByName_Empty() {
+ assertNull(h2PlayerService.getByName(""));
+ }
+
+ @Test
+ void getByName_Null() {
+ assertNull(h2PlayerService.getByName(null));
+ }
+
+ @Test
+ void getById() {
+ H2Player player = h2PlayerService.save(buildPlayer());
+
+ H2Player player2 = h2PlayerService.getById(player.getId());
+ assertEquals(player, player2);
+ }
+
+ @Test
+ void getById_NotExists() {
+ assertNull(h2PlayerService.getById(9999));
+ }
+}
diff --git a/proto_1.12.2/build.gradle b/proto_1.12.2/build.gradle
index 463d1fb..8e3a7e2 100644
--- a/proto_1.12.2/build.gradle
+++ b/proto_1.12.2/build.gradle
@@ -1,4 +1,3 @@
-group 'mc'
version '1.0-SNAPSHOT'
dependencies {
@@ -6,6 +5,5 @@ dependencies {
compile_excludeCopy project(':core')
/* Components */
- compile (group: 'com.google.guava', name: 'guava', version: '24.1-jre')
- compile (group: 'com.google.code.gson', name: 'gson', version: '2.8.2')
+ compile (group: 'com.google.code.gson', name: 'gson', version: '2.8.5')
}
diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetInputStream_p340.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetInputStream_p340.java
index 418f9aa..84bbd08 100644
--- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetInputStream_p340.java
+++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetInputStream_p340.java
@@ -13,25 +13,31 @@ import java.util.UUID;
@Slf4j
public abstract class NetInputStream_p340 extends NetInputStream {
@Override
- public int readVarInt() {
+ public int readVarInt(int[] countReadBytes) {
int numRead = 0;
int result = 0;
byte read;
do {
+ if ((numRead+1) > 5) {
+ log.warn("VarInt is too big");
+ break;
+ }
read = readByte();
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
numRead++;
- if (numRead > 5) {
- log.warn("VarInt is too big");
- break;
- }
} while ((read & 0b10000000) != 0);
+ if (countReadBytes != null && countReadBytes.length == 1) countReadBytes[0] = numRead;
return result;
}
+ @Override
+ public int readVarInt() {
+ return readVarInt(null);
+ }
+
@Override
public String readString() {
int size = readVarInt();
diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/TeleportManager.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/TeleportManager.java
index 951f098..ba9cd07 100644
--- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/TeleportManager.java
+++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/TeleportManager.java
@@ -45,8 +45,7 @@ public class TeleportManager {
public void apply(int teleportId) {
if (teleportMap.containsKey(teleportId)) {
TpData data = teleportMap.remove(teleportId);
- data.player.getLocation().setXYZ(data.newLocation);
- data.player.getLocation().setYawPitch(data.newLocation);
+ data.player.getLocation().set(data.newLocation);
}
}
diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java
index 6600db6..38db3cc 100644
--- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java
+++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java
@@ -4,7 +4,6 @@
*/
package mc.core.network.proto_1_12_2.packets;
-import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -17,7 +16,6 @@ import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkSection;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerAbilitiesPacket.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerAbilitiesPacket.java
index b3a8312..6258bc5 100644
--- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerAbilitiesPacket.java
+++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerAbilitiesPacket.java
@@ -18,6 +18,11 @@ import mc.core.network.SCPacket;
@Setter
@ToString
public class PlayerAbilitiesPacket implements SCPacket, CSPacket {
+ private static final byte $GOD_MODE_MASK = 0x01,
+ $FLYING_MASK = 0x02,
+ $CAN_FLY_MASK = 0x04,
+ $IDB_MASK = 0x08;
+
private boolean godMode = false;
private boolean flying = false;
private boolean canFly = false;
@@ -29,10 +34,10 @@ public class PlayerAbilitiesPacket implements SCPacket, CSPacket {
@Override
public void writeSelf(NetOutputStream netStream) {
byte flag = 0;
- if (godMode) flag = (byte)(flag | 0x01);
- if (flying) flag = (byte)(flag | 0x02);
- if (canFly) flag = (byte)(flag | 0x04);
- if (instantDestroyBlocks) flag = (byte)(flag | 0x08);
+ if (godMode) flag = (byte)(flag | $GOD_MODE_MASK);
+ if (flying) flag = (byte)(flag | $FLYING_MASK);
+ if (canFly) flag = (byte)(flag | $CAN_FLY_MASK);
+ if (instantDestroyBlocks) flag = (byte)(flag | $IDB_MASK);
netStream.writeByte(flag);
netStream.writeFloat(flyingSpeed);
@@ -42,11 +47,10 @@ public class PlayerAbilitiesPacket implements SCPacket, CSPacket {
@Override
public void readSelf(NetInputStream netStream) {
byte flag = netStream.readByte();
- //FIXME треубет проверки
- godMode = (flag == 0x08);
- canFly = (flag == 0x04);
- flying = (flag == 0x02);
- instantDestroyBlocks = (flag == 0x01);
+ godMode = (flag & $GOD_MODE_MASK) > 0;
+ canFly = (flag & $CAN_FLY_MASK) > 0;
+ flying = (flag & $FLYING_MASK) > 0;
+ instantDestroyBlocks = (flag & $IDB_MASK) > 0;
flyingSpeed = netStream.readFloat();
walkingSpeed = netStream.readFloat();
diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerBlockPlacementPacket.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerBlockPlacementPacket.java
index 1a1ac1d..ebcf00d 100644
--- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerBlockPlacementPacket.java
+++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerBlockPlacementPacket.java
@@ -2,16 +2,16 @@ package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.ToString;
-import mc.core.Location;
+import mc.core.network.proto_1_12_2.serializers.BlockLocationSerializer;
+import mc.core.world.block.BlockLocation;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.network.proto_1_12_2.Direction;
-import mc.core.utils.CompactedCoords;
@Getter
@ToString
public class PlayerBlockPlacementPacket implements CSPacket {
- private Location location;
+ private BlockLocation location;
private Direction face;
/** true - main hand; false - off hand */
private boolean hand;
@@ -20,8 +20,7 @@ public class PlayerBlockPlacementPacket implements CSPacket {
@Override
public void readSelf(NetInputStream netStream) {
long compactedCoords = netStream.readLong();
- double[] xyz = CompactedCoords.uncompressXYZ(compactedCoords);
- location = new Location(xyz[0], xyz[1], xyz[2]);
+ location = BlockLocationSerializer.fromLong(compactedCoords);
face = Direction.getById(netStream.readVarInt());
hand = (netStream.readVarInt() == 1);
cursorX = netStream.readFloat();
diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerDiggingPacket.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerDiggingPacket.java
index a2bde1e..355cc57 100644
--- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerDiggingPacket.java
+++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerDiggingPacket.java
@@ -3,11 +3,11 @@ package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
-import mc.core.Location;
+import mc.core.network.proto_1_12_2.serializers.BlockLocationSerializer;
+import mc.core.world.block.BlockLocation;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.network.proto_1_12_2.Direction;
-import mc.core.utils.CompactedCoords;
import java.util.Arrays;
@@ -42,15 +42,14 @@ public class PlayerDiggingPacket implements CSPacket {
}
private Status status;
- private Location location;
+ private BlockLocation location;
private Direction face;
@Override
public void readSelf(NetInputStream netStream) {
status = Status.getById(netStream.readVarInt());
long compactCoord = netStream.readLong();
- double[] xyz = CompactedCoords.uncompressXYZ(compactCoord);
- location = new Location(xyz[0], xyz[1], xyz[2]);
+ location = BlockLocationSerializer.fromLong(compactCoord);
face = Direction.getById(netStream.readByte());
}
}
diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerPositionAndLookPacket.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerPositionAndLookPacket.java
index 985d8df..3c783a7 100644
--- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerPositionAndLookPacket.java
+++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerPositionAndLookPacket.java
@@ -48,8 +48,7 @@ public class PlayerPositionAndLookPacket implements SCPacket, CSPacket {
netStream.readDouble(),
netStream.readDouble(),
netStream.readFloat(),
- netStream.readFloat(),
- null
+ netStream.readFloat()
);
this.onGround = netStream.readBoolean();
diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/TabCompletePacket.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/TabCompletePacket.java
index 09f686d..558e8dc 100644
--- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/TabCompletePacket.java
+++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/TabCompletePacket.java
@@ -4,7 +4,7 @@
*/
package mc.core.network.proto_1_12_2.packets;
-import mc.core.Location;
+import mc.core.world.block.BlockLocation;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
@@ -12,7 +12,7 @@ public class TabCompletePacket implements CSPacket {
private String text;
private boolean assumeCommand;
private boolean hasPosition;
- private Location location;
+ private BlockLocation location;
@Override
public void readSelf(NetInputStream netStream) {
@@ -27,7 +27,7 @@ public class TabCompletePacket implements CSPacket {
double y = (compactValue >> 26) & 0xFFF;
double z = compactValue << 38 >> 38; // is normal?
- this.location = new Location(x, y, z);
+ this.location = new BlockLocation((int)x, (int)y, (int)z); //FIXME
}
}
}
diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/serializers/BlockLocationSerializer.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/serializers/BlockLocationSerializer.java
new file mode 100644
index 0000000..9aea2f5
--- /dev/null
+++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/serializers/BlockLocationSerializer.java
@@ -0,0 +1,63 @@
+package mc.core.network.proto_1_12_2.serializers;
+
+import mc.core.world.block.BlockLocation;
+
+import static com.google.common.math.IntMath.isPowerOfTwo;
+
+public class BlockLocationSerializer {
+ private static final int[] MULTIPLY_DE_BRUIJN_BIT_POSITION = new int[] {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
+ private static final int NUM_X_BITS = 1 + log2(smallestEncompassingPowerOfTwo(30000000));
+ private static final int NUM_Z_BITS = NUM_X_BITS;
+ private static final int NUM_Y_BITS = 64 - NUM_X_BITS - NUM_Z_BITS;
+ private static final int Y_SHIFT = NUM_Z_BITS;
+ private static final int X_SHIFT = Y_SHIFT + NUM_Y_BITS;
+ private static final long X_MASK = (1L << NUM_X_BITS) - 1L;
+ private static final long Y_MASK = (1L << NUM_Y_BITS) - 1L;
+ private static final long Z_MASK = (1L << NUM_Z_BITS) - 1L;
+
+ /*
+ * net.minecraft.util.math.MathHelper#log2(int)
+ */
+ private static int log2(int value) {
+ return log2DeBruijn(value) - (isPowerOfTwo(value) ? 0 : 1);
+ }
+
+ /*
+ * net.minecraft.util.math.MathHelper#log2DeBruijn(int)
+ */
+ private static int log2DeBruijn(int value) {
+ value = isPowerOfTwo(value) ? value : smallestEncompassingPowerOfTwo(value);
+ return MULTIPLY_DE_BRUIJN_BIT_POSITION[(int)((long)value * 125613361L >> 27) & 31];
+ }
+
+ /*
+ * net.minecraft.util.math.MathHelper#smallestEncompassingPowerOfTwo(int)
+ */
+ private static int smallestEncompassingPowerOfTwo(int value) {
+ int i = value - 1;
+ i = i | i >> 1;
+ i = i | i >> 2;
+ i = i | i >> 4;
+ i = i | i >> 8;
+ i = i | i >> 16;
+ return i + 1;
+ }
+
+ public static long toLong(BlockLocation location) {
+ return ((long)location.getX() & X_MASK) << X_SHIFT |
+ ((long)location.getY() & Y_MASK) << Y_SHIFT |
+ ((long)location.getZ() & Z_MASK);
+ }
+
+ public static BlockLocation fromLong(long value) {
+ BlockLocation location = BlockLocation.ZERO();
+ fromLong(value, location);
+ return location;
+ }
+
+ public static void fromLong(long value, BlockLocation location) {
+ location.setX((int)(value << 64 - X_SHIFT - NUM_X_BITS >> 64 - NUM_X_BITS));
+ location.setY((int)(value << 64 - Y_SHIFT - NUM_Y_BITS >> 64 - NUM_Y_BITS));
+ location.setZ((int)(value << 64 - NUM_Z_BITS >> 64 - NUM_Z_BITS));
+ }
+}
diff --git a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStream.java b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStream.java
new file mode 100644
index 0000000..c51beaf
--- /dev/null
+++ b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStream.java
@@ -0,0 +1,72 @@
+package mc.core.network.proto_1_12_2.packets;
+
+import mc.core.network.proto_1_12_2.NetInputStream_p340;
+
+import java.io.ByteArrayInputStream;
+
+public class ByteArrayInputNetStream extends NetInputStream_p340 {
+ private ByteArrayInputStream bais;
+
+ public ByteArrayInputNetStream(byte[] buff) {
+ bais = new ByteArrayInputStream(buff);
+ }
+
+ @Override
+ public boolean readBoolean() {
+ return false;
+ }
+
+ @Override
+ public byte readByte() {
+ return (byte) bais.read();
+ }
+
+ @Override
+ public void readBytes(byte[] buffer) {
+ }
+
+ @Override
+ public int readUnsignedByte() {
+ return 0;
+ }
+
+ @Override
+ public int readUnsignedShort() {
+ return 0;
+ }
+
+ @Override
+ public short readShort() {
+ return 0;
+ }
+
+ @Override
+ public int readInt() {
+ int ch1 = bais.read();
+ int ch2 = bais.read();
+ int ch3 = bais.read();
+ int ch4 = bais.read();
+ if ((ch1 | ch2 | ch3 | ch4) < 0) return 0;
+ return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
+ }
+
+ @Override
+ public long readLong() {
+ return 0;
+ }
+
+ @Override
+ public float readFloat() {
+ return Float.intBitsToFloat(readInt());
+ }
+
+ @Override
+ public double readDouble() {
+ return 0;
+ }
+
+ @Override
+ public void skipBytes(int count) {
+
+ }
+}
diff --git a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStreamTest.java b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStreamTest.java
new file mode 100644
index 0000000..4964a77
--- /dev/null
+++ b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStreamTest.java
@@ -0,0 +1,48 @@
+package mc.core.network.proto_1_12_2.packets;
+
+import mc.core.network.proto_1_12_2.ByteArrayOutputNetStream;
+import org.junit.jupiter.api.Test;
+
+import java.util.Random;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ByteArrayInputNetStreamTest {
+ private Random rnd = new Random();
+
+ @Test
+ void readByte() {
+ final byte b0 = (byte) rnd.nextInt();
+ ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream();
+ netStream.writeByte(b0);
+ byte[] buffer = netStream.toByteArray();
+
+ ByteArrayInputNetStream netInputStream = new ByteArrayInputNetStream(buffer);
+ byte b1 = netInputStream.readByte();
+ assertEquals(b0, b1);
+ }
+
+ @Test
+ void readInt() {
+ final int i0 = rnd.nextInt();
+ ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream();
+ netStream.writeInt(i0);
+ byte[] buffer = netStream.toByteArray();
+
+ ByteArrayInputNetStream netInputStream = new ByteArrayInputNetStream(buffer);
+ int i1 = netInputStream.readInt();
+ assertEquals(i0, i1);
+ }
+
+ @Test
+ void readFloat() {
+ final float f0 = rnd.nextFloat();
+ ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream();
+ netStream.writeFloat(f0);
+ byte[] buffer = netStream.toByteArray();
+
+ ByteArrayInputNetStream netInputStream = new ByteArrayInputNetStream(buffer);
+ float f1 = netInputStream.readFloat();
+ assertEquals(f0, f1, 0.00001f);
+ }
+}
\ No newline at end of file
diff --git a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/TestChunkdataPacket.java b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ChunkdataPacketTest.java
similarity index 86%
rename from proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/TestChunkdataPacket.java
rename to proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ChunkdataPacketTest.java
index 449a672..5060676 100644
--- a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/TestChunkdataPacket.java
+++ b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ChunkdataPacketTest.java
@@ -9,31 +9,31 @@ import mc.core.world.block.BlockFactory;
import mc.core.world.block.BlockType;
import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkSection;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-public class TestChunkdataPacket {
+class ChunkdataPacketTest {
private static byte[] expectedPacketData;
private World world;
- @BeforeClass
- public static void beforeClassTest() throws IOException {
- InputStream inputStream = TestChunkdataPacket.class.getResourceAsStream("ChunkDataPacket.bin");
+ @BeforeAll
+ static void beforeClassTest() throws IOException {
+ InputStream inputStream = ChunkdataPacketTest.class.getResourceAsStream("ChunkDataPacket.bin");
expectedPacketData = ByteStreams.toByteArray(inputStream);
}
- @Before
- public void prepareWorld() {
+ @BeforeEach
+ void prepareWorld() {
final ChunkSection chunkSection = mock(ChunkSection.class);
when(chunkSection.getSkyLight(anyInt(), anyInt(), anyInt())).thenAnswer(invocation -> {
int y = (int)invocation.getArguments()[1];
@@ -77,7 +77,7 @@ public class TestChunkdataPacket {
}
@Test
- public void test() {
+ void writePacket() {
ChunkDataPacket packet = new ChunkDataPacket();
packet.setX(0);
packet.setZ(0);
diff --git a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/PlayerAbilitiesPacketTest.java b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/PlayerAbilitiesPacketTest.java
new file mode 100644
index 0000000..229a15a
--- /dev/null
+++ b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/PlayerAbilitiesPacketTest.java
@@ -0,0 +1,40 @@
+package mc.core.network.proto_1_12_2.packets;
+
+import mc.core.network.proto_1_12_2.ByteArrayOutputNetStream;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Random;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class PlayerAbilitiesPacketTest {
+ private Random rnd = new Random();
+ private PlayerAbilitiesPacket packet;
+
+ @BeforeEach
+ void before() {
+ packet = new PlayerAbilitiesPacket();
+ packet.setGodMode(rnd.nextBoolean());
+ packet.setFlying(rnd.nextBoolean());
+ packet.setCanFly(rnd.nextBoolean());
+ packet.setInstantDestroyBlocks(rnd.nextBoolean());
+ packet.setFlyingSpeed(rnd.nextFloat());
+ }
+
+ @Test
+ void writePacket() {
+ ByteArrayOutputNetStream netOutputStream = new ByteArrayOutputNetStream();
+ packet.writeSelf(netOutputStream);
+
+ ByteArrayInputNetStream netInputStream = new ByteArrayInputNetStream(netOutputStream.toByteArray());
+ PlayerAbilitiesPacket outPkt = new PlayerAbilitiesPacket();
+ outPkt.readSelf(netInputStream);
+
+ assertEquals(packet.isGodMode(), outPkt.isGodMode(), "god mode");
+ assertEquals(packet.isFlying(), outPkt.isFlying(), "flying");
+ assertEquals(packet.isCanFly(), outPkt.isCanFly(), "can fly");
+ assertEquals(packet.isInstantDestroyBlocks(), outPkt.isInstantDestroyBlocks(), "instant destroy block");
+ assertEquals(packet.getFlyingSpeed(), outPkt.getFlyingSpeed(), 0.00001f, "flying speed");
+ }
+}
diff --git a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/serializers/BlockLocationSerializerTest.java b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/serializers/BlockLocationSerializerTest.java
new file mode 100644
index 0000000..dda7500
--- /dev/null
+++ b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/serializers/BlockLocationSerializerTest.java
@@ -0,0 +1,32 @@
+package mc.core.network.proto_1_12_2.serializers;
+
+import mc.core.world.block.BlockLocation;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class BlockLocationSerializerTest {
+ private static final ThreadLocalRandom rnd = ThreadLocalRandom.current();
+ private static final int minI = 0, maxI = 10;
+ private int x, y, z;
+
+ @BeforeEach
+ void before() {
+ x = rnd.nextInt(minI, maxI);
+ y = rnd.nextInt(minI, maxI);
+ z = rnd.nextInt(minI, maxI);
+ }
+
+ @Test
+ void serialize() {
+ BlockLocation location = new BlockLocation(x, y, z);
+ final long serializedCoords = BlockLocationSerializer.toLong(location);
+
+ BlockLocation deserLoc = BlockLocationSerializer.fromLong(serializedCoords);
+
+ assertEquals(location, deserLoc);
+ }
+}
\ No newline at end of file
diff --git a/proto_1.12.2_netty/build.gradle b/proto_1.12.2_netty/build.gradle
index 9704176..0e9cbf0 100644
--- a/proto_1.12.2_netty/build.gradle
+++ b/proto_1.12.2_netty/build.gradle
@@ -1,4 +1,3 @@
-group 'mc'
version '1.0-SNAPSHOT'
ext {
diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/KeepAliveThread.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/KeepAliveThread.java
new file mode 100644
index 0000000..afa4607
--- /dev/null
+++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/KeepAliveThread.java
@@ -0,0 +1,49 @@
+package mc.core.network.proto_1_12_2.netty;
+
+import lombok.Getter;
+import lombok.Setter;
+import mc.core.network.proto_1_12_2.packets.KeepAlivePacket;
+import mc.core.player.PlayerManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Random;
+
+@Component
+public class KeepAliveThread extends Thread {
+ private static final Random RAND = new Random();
+ private final Object lock = new Object();
+ @Autowired
+ private PlayerManager playerManager;
+ @Setter
+ private int interval = 10;
+
+ public void notifyLock() {
+ synchronized (lock) {
+ lock.notify();
+ }
+ }
+
+ @Override
+ public void run() {
+ while (!Thread.currentThread().isInterrupted()) {
+ while(playerManager.getCountPlayers() == 0) {
+ synchronized (lock) {
+ try {
+ lock.wait();
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+
+ playerManager.getBroadcastChannel().writeAndFlush(new KeepAlivePacket(RAND.nextLong()));
+
+ try {
+ Thread.sleep(interval);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+}
diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/NettyServer.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/NettyServer.java
index 79be3f9..7df1c3f 100644
--- a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/NettyServer.java
+++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/NettyServer.java
@@ -1,10 +1,7 @@
-/*
- * DmitriyMX
- * 2018-06-10
- */
package mc.core.network.proto_1_12_2.netty;
import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
@@ -14,7 +11,7 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.AttributeKey;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
-import mc.core.events.EventBusGetter;
+import mc.core.eventbus.EventBusGetter;
import mc.core.network.Server;
import mc.core.network.StartServerException;
import mc.core.network.proto_1_12_2.State;
@@ -22,16 +19,20 @@ import mc.core.network.proto_1_12_2.packets.StatusResponsePacket;
import mc.core.player.Player;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Component;
import java.util.Map;
@Slf4j
+@Component
public class NettyServer implements Server {
public static final AttributeKey ATTR_STATE = AttributeKey.newInstance("ATTR_STATE");
public static final AttributeKey ATTR_PLAYER = AttributeKey.newInstance("ATTR_PLAYER");
@Autowired
- private ApplicationContext applicationContext;
+ private ApplicationContext context;
+ @Autowired
+ private KeepAliveThread keepAliveThread;
@Setter
private String host;
@Setter
@@ -44,7 +45,7 @@ public class NettyServer implements Server {
return new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) {
- Map beans = applicationContext.getBeansOfType(ChannelHandler.class);
+ Map beans = context.getBeansOfType(ChannelHandler.class);
beans.forEach(socketChannel.pipeline()::addLast);
}
};
@@ -64,7 +65,7 @@ public class NettyServer implements Server {
public void start() throws StartServerException {
log.info("Use protocol {}", StatusResponsePacket.NAME);
- EventBusGetter.INSTANCE.register(new PlayerEventListener());
+ EventBusGetter.getInstance().register(new PlayerEventListener());
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup(workerGroupCount);
@@ -73,7 +74,9 @@ public class NettyServer implements Server {
log.info("Start server: {}:{}", host, port);
try {
- serverBootstrap.bind(host, port).sync().channel().closeFuture().sync();
+ ChannelFuture channelFuture = serverBootstrap.bind(host, port).sync();
+ keepAliveThread.start();
+ channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
throw new StartServerException(e);
}
@@ -82,6 +85,7 @@ public class NettyServer implements Server {
@Override
public void stop() {
log.info("Server shutdown");
+ keepAliveThread.interrupt();
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/PacketDecoder.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/PacketDecoder.java
index 57b1832..6115ced 100644
--- a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/PacketDecoder.java
+++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/PacketDecoder.java
@@ -1,12 +1,8 @@
-/*
- * DmitriyMX
- * 2018-06-10
- */
package mc.core.network.proto_1_12_2.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.handler.codec.ReplayingDecoder;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
@@ -18,7 +14,9 @@ import java.util.List;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_STATE;
@Slf4j
-public class PacketDecoder extends ByteToMessageDecoder {
+public class PacketDecoder extends ReplayingDecoder {
+ private int[] countReadBytes = new int[]{0};
+
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.channel().attr(ATTR_STATE).set(State.HANDSHAKE);
@@ -33,27 +31,25 @@ public class PacketDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List