mirror of
https://github.com/ViaVersion/ViaVersion.git
synced 2024-12-23 08:57:39 +01:00
Intitial APIv2, this does not work. Type conversion is not implemented, as well as quite a few things, most things will probably change.
This commit is contained in:
parent
131517105e
commit
7cff20f218
@ -60,6 +60,7 @@ public enum PacketType {
|
|||||||
PLAY_SPAWN_MOB(State.PLAY, Direction.OUTGOING, 0x0F, 0x03),
|
PLAY_SPAWN_MOB(State.PLAY, Direction.OUTGOING, 0x0F, 0x03),
|
||||||
PLAY_SPAWN_PAINTING(State.PLAY, Direction.OUTGOING, 0x10, 0x04),
|
PLAY_SPAWN_PAINTING(State.PLAY, Direction.OUTGOING, 0x10, 0x04),
|
||||||
PLAY_SPAWN_PLAYER(State.PLAY, Direction.OUTGOING, 0x0C, 0x05),
|
PLAY_SPAWN_PLAYER(State.PLAY, Direction.OUTGOING, 0x0C, 0x05),
|
||||||
|
|
||||||
PLAY_ANIMATION(State.PLAY, Direction.OUTGOING, 0x0B, 0x06),
|
PLAY_ANIMATION(State.PLAY, Direction.OUTGOING, 0x0B, 0x06),
|
||||||
PLAY_STATS(State.PLAY, Direction.OUTGOING, 0x37, 0x07),
|
PLAY_STATS(State.PLAY, Direction.OUTGOING, 0x37, 0x07),
|
||||||
PLAY_BLOCK_BREAK_ANIMATION(State.PLAY, Direction.OUTGOING, 0x25, 0x08),
|
PLAY_BLOCK_BREAK_ANIMATION(State.PLAY, Direction.OUTGOING, 0x25, 0x08),
|
||||||
|
57
src/main/java/us/myles/ViaVersion2/api/PacketWrapper.java
Normal file
57
src/main/java/us/myles/ViaVersion2/api/PacketWrapper.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package us.myles.ViaVersion2.api;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.data.UserConnection;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
import us.myles.ViaVersion2.api.util.Pair;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PacketWrapper {
|
||||||
|
private final ByteBuf inputBuffer;
|
||||||
|
private final UserConnection userConnection;
|
||||||
|
private List<Pair<Type, Object>> packetValues = new ArrayList<>();
|
||||||
|
|
||||||
|
public PacketWrapper(ByteBuf inputBuffer, UserConnection userConnection) {
|
||||||
|
this.inputBuffer = inputBuffer;
|
||||||
|
this.userConnection = userConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T get(Type<T> type, int index) {
|
||||||
|
int currentIndex = 0;
|
||||||
|
for (Pair<Type, Object> packetValue : packetValues) {
|
||||||
|
if (packetValue.getKey() == type) { // Ref check
|
||||||
|
if (currentIndex == index) {
|
||||||
|
return (T) packetValue.getValue();
|
||||||
|
}
|
||||||
|
currentIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T read(Type<T> type) {
|
||||||
|
// We could in the future log input read values, but honestly for things like bulk maps, mem waste D:
|
||||||
|
return type.read(inputBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public <T> void write(Type<T> type, T value) {
|
||||||
|
packetValues.add(new Pair<Type, Object>(type, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeToBuffer(ByteBuf buffer) {
|
||||||
|
for (Pair<Type, Object> packetValue : packetValues) {
|
||||||
|
packetValue.getKey().write(buffer, packetValue.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeRemaining(ByteBuf output) {
|
||||||
|
output.writeBytes(inputBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserConnection user() {
|
||||||
|
return this.userConnection;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package us.myles.ViaVersion2.api.data;
|
||||||
|
|
||||||
|
public class StoredObject {
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package us.myles.ViaVersion2.api.data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UserConnection {
|
||||||
|
List<StoredObject> storedObjects = new ArrayList<>();
|
||||||
|
|
||||||
|
public <T extends StoredObject> T object(Class<T> objectClass) {
|
||||||
|
for (StoredObject o : storedObjects) {
|
||||||
|
if (o.getClass().equals(objectClass))
|
||||||
|
return (T) o;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends StoredObject> boolean has(Class<T> objectClass) {
|
||||||
|
for (StoredObject o : storedObjects) {
|
||||||
|
if (o.getClass().equals(objectClass))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(StoredObject object) {
|
||||||
|
storedObjects.add(object);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package us.myles.ViaVersion2.api.protocol;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import us.myles.ViaVersion.packets.Direction;
|
||||||
|
import us.myles.ViaVersion.packets.State;
|
||||||
|
import us.myles.ViaVersion2.api.PacketWrapper;
|
||||||
|
import us.myles.ViaVersion2.api.util.Pair;
|
||||||
|
import us.myles.ViaVersion2.api.data.UserConnection;
|
||||||
|
import us.myles.ViaVersion2.api.remapper.PacketRemapper;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public abstract class Protocol {
|
||||||
|
public abstract void registerPackets();
|
||||||
|
|
||||||
|
public abstract void init(UserConnection userConnection);
|
||||||
|
|
||||||
|
public Map<Pair<State, Integer>, ProtocolPacket> incoming = new HashMap<>();
|
||||||
|
public Map<Pair<State, Integer>, ProtocolPacket> outgoing = new HashMap<>();
|
||||||
|
|
||||||
|
public void registerIncoming(State state, int oldPacketID, int newPacketID) {
|
||||||
|
registerIncoming(state, oldPacketID, newPacketID, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerIncoming(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) {
|
||||||
|
ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper);
|
||||||
|
incoming.put(new Pair<>(state, newPacketID), protocolPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerOutgoing(State state, int oldPacketID, int newPacketID) {
|
||||||
|
registerOutgoing(state, oldPacketID, newPacketID, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerOutgoing(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) {
|
||||||
|
ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper);
|
||||||
|
outgoing.put(new Pair<>(state, oldPacketID), protocolPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void transform(Direction direction, State state, int packetID, PacketWrapper packetWrapper, ByteBuf output) {
|
||||||
|
Pair<State, Integer> statePacket = new Pair<>(state, packetID);
|
||||||
|
Map<Pair<State, Integer>, ProtocolPacket> packetMap = (direction == Direction.OUTGOING ? outgoing : incoming);
|
||||||
|
ProtocolPacket protocolPacket;
|
||||||
|
if (packetMap.containsKey(statePacket)) {
|
||||||
|
protocolPacket = packetMap.get(statePacket);
|
||||||
|
} else {
|
||||||
|
System.out.println("Packet not found: " + packetID);
|
||||||
|
// simply translate
|
||||||
|
Type.VAR_INT.write(output, packetID);
|
||||||
|
// pass through
|
||||||
|
packetWrapper.writeRemaining(output);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// write packet id
|
||||||
|
Type.VAR_INT.write(output, direction == Direction.OUTGOING ? protocolPacket.getNewID() : protocolPacket.getOldID());
|
||||||
|
// remap
|
||||||
|
if (protocolPacket.getRemapper() != null) {
|
||||||
|
protocolPacket.getRemapper().remap(packetWrapper);
|
||||||
|
// write to output
|
||||||
|
packetWrapper.writeToBuffer(output);
|
||||||
|
} else {
|
||||||
|
// pass through
|
||||||
|
packetWrapper.writeRemaining(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
class ProtocolPacket {
|
||||||
|
State state;
|
||||||
|
int oldID;
|
||||||
|
int newID;
|
||||||
|
PacketRemapper remapper;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package us.myles.ViaVersion2.api.protocol1_9to1_8;
|
||||||
|
|
||||||
|
import us.myles.ViaVersion2.api.data.UserConnection;
|
||||||
|
import us.myles.ViaVersion2.api.protocol.Protocol;
|
||||||
|
import us.myles.ViaVersion2.api.protocol1_9to1_8.packets.SpawnPackets;
|
||||||
|
import us.myles.ViaVersion2.api.protocol1_9to1_8.storage.EntityTracker;
|
||||||
|
|
||||||
|
public class Protocol1_9TO1_8 extends Protocol {
|
||||||
|
@Override
|
||||||
|
public void registerPackets() {
|
||||||
|
// Example PLAY_SPAWN_OBJECT(State.PLAY, Direction.OUTGOING, 0x0E, 0x00),
|
||||||
|
SpawnPackets.register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(UserConnection userConnection) {
|
||||||
|
// Entity tracker
|
||||||
|
userConnection.add(new EntityTracker());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,161 @@
|
|||||||
|
package us.myles.ViaVersion2.api.protocol1_9to1_8.packets;
|
||||||
|
|
||||||
|
import us.myles.ViaVersion.packets.State;
|
||||||
|
import us.myles.ViaVersion2.api.PacketWrapper;
|
||||||
|
import us.myles.ViaVersion2.api.protocol.Protocol;
|
||||||
|
import us.myles.ViaVersion2.api.protocol1_9to1_8.storage.EntityTracker;
|
||||||
|
import us.myles.ViaVersion2.api.remapper.PacketRemapper;
|
||||||
|
import us.myles.ViaVersion2.api.remapper.ValueCreator;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class SpawnPackets {
|
||||||
|
public static void register(Protocol protocol) {
|
||||||
|
// Spawn Object Packet
|
||||||
|
protocol.registerOutgoing(State.PLAY, 0x0E, 0x00, new PacketRemapper() {
|
||||||
|
@Override
|
||||||
|
public void registerMap() {
|
||||||
|
map(Type.VAR_INT); // 0 - Entity ID
|
||||||
|
// 1 - UUID
|
||||||
|
create(new ValueCreator() {
|
||||||
|
@Override
|
||||||
|
public void write(PacketWrapper wrapper) {
|
||||||
|
int entityID = wrapper.get(Type.VAR_INT, 0);
|
||||||
|
EntityTracker tracker = wrapper.user().object(EntityTracker.class);
|
||||||
|
wrapper.write(Type.UUID, tracker.getEntityUUID(entityID));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
map(Type.BYTE); // 2 - Type
|
||||||
|
|
||||||
|
map(Type.INT, Type.DOUBLE); // 3 - X - Needs to be divide by 32
|
||||||
|
map(Type.INT, Type.DOUBLE); // 4 - Y - Needs to be divide by 32
|
||||||
|
map(Type.INT, Type.DOUBLE); // 5 - Z - Needs to be divide by 32
|
||||||
|
|
||||||
|
map(Type.BYTE); // 6 - Pitch
|
||||||
|
map(Type.BYTE); // 7 - Yaw
|
||||||
|
|
||||||
|
map(Type.INT); // 8 - Data
|
||||||
|
|
||||||
|
// Create last 3 shorts
|
||||||
|
create(new ValueCreator() {
|
||||||
|
@Override
|
||||||
|
public void write(PacketWrapper wrapper) {
|
||||||
|
int data = wrapper.get(Type.INT, 3); // Data (4th Integer)
|
||||||
|
|
||||||
|
short vX = 0, vY = 0, vZ = 0;
|
||||||
|
if (data > 0) {
|
||||||
|
vX = wrapper.read(Type.SHORT);
|
||||||
|
vY = wrapper.read(Type.SHORT);
|
||||||
|
vZ = wrapper.read(Type.SHORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.write(Type.SHORT, vX);
|
||||||
|
wrapper.write(Type.SHORT, vY);
|
||||||
|
wrapper.write(Type.SHORT, vZ);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spawn XP Packet
|
||||||
|
protocol.registerOutgoing(State.PLAY, 0x11, 0x01, new PacketRemapper() {
|
||||||
|
@Override
|
||||||
|
public void registerMap() {
|
||||||
|
map(Type.VAR_INT); // 0 - Entity ID
|
||||||
|
|
||||||
|
map(Type.INT, Type.DOUBLE); // 1 - X - Needs to be divide by 32
|
||||||
|
map(Type.INT, Type.DOUBLE); // 2 - Y - Needs to be divide by 32
|
||||||
|
map(Type.INT, Type.DOUBLE); // 3 - Z - Needs to be divide by 32
|
||||||
|
|
||||||
|
map(Type.INT); // 4 - Data
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spawn Global Entity Packet
|
||||||
|
protocol.registerOutgoing(State.PLAY, 0x2C, 0x02, new PacketRemapper() {
|
||||||
|
@Override
|
||||||
|
public void registerMap() {
|
||||||
|
map(Type.VAR_INT); // 0 - Entity ID
|
||||||
|
map(Type.BYTE); // 1 - Type
|
||||||
|
|
||||||
|
map(Type.INT, Type.DOUBLE); // 2 - X - Needs to be divide by 32
|
||||||
|
map(Type.INT, Type.DOUBLE); // 3 - Y - Needs to be divide by 32
|
||||||
|
map(Type.INT, Type.DOUBLE); // 4 - Z - Needs to be divide by 32
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spawn Mob Packet
|
||||||
|
protocol.registerOutgoing(State.PLAY, 0x0F, 0x03, new PacketRemapper() {
|
||||||
|
@Override
|
||||||
|
public void registerMap() {
|
||||||
|
map(Type.VAR_INT); // 0 - Entity ID
|
||||||
|
// 1 - UUID
|
||||||
|
create(new ValueCreator() {
|
||||||
|
@Override
|
||||||
|
public void write(PacketWrapper wrapper) {
|
||||||
|
int entityID = wrapper.get(Type.VAR_INT, 0);
|
||||||
|
EntityTracker tracker = wrapper.user().object(EntityTracker.class);
|
||||||
|
wrapper.write(Type.UUID, tracker.getEntityUUID(entityID));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
map(Type.UNSIGNED_BYTE); // 2 - Type
|
||||||
|
|
||||||
|
map(Type.INT, Type.DOUBLE); // 3 - X - Needs to be divide by 32
|
||||||
|
map(Type.INT, Type.DOUBLE); // 4 - Y - Needs to be divide by 32
|
||||||
|
map(Type.INT, Type.DOUBLE); // 5 - Z - Needs to be divide by 32
|
||||||
|
|
||||||
|
map(Type.BYTE); // 6 - Yaw
|
||||||
|
map(Type.BYTE); // 7 - Pitch
|
||||||
|
map(Type.BYTE); // 8 - Head Pitch
|
||||||
|
|
||||||
|
map(Type.SHORT); // 9 - Velocity X
|
||||||
|
map(Type.SHORT); // 10 - Velocity Y
|
||||||
|
map(Type.SHORT); // 11 - Velocity Z
|
||||||
|
|
||||||
|
// TODO Read Metadata
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spawn Painting Packet
|
||||||
|
protocol.registerOutgoing(State.PLAY, 0x10, 0x04, new PacketRemapper() {
|
||||||
|
@Override
|
||||||
|
public void registerMap() {
|
||||||
|
map(Type.VAR_INT); // 0 - Entity ID
|
||||||
|
// 1 - UUID
|
||||||
|
create(new ValueCreator() {
|
||||||
|
@Override
|
||||||
|
public void write(PacketWrapper wrapper) {
|
||||||
|
int entityID = wrapper.get(Type.VAR_INT, 0);
|
||||||
|
EntityTracker tracker = wrapper.user().object(EntityTracker.class);
|
||||||
|
wrapper.write(Type.UUID, tracker.getEntityUUID(entityID));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
map(Type.STRING); // 2 - Title
|
||||||
|
|
||||||
|
map(Type.POSITION); // 3 - Position
|
||||||
|
|
||||||
|
map(Type.BYTE); // 4 - Direction
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spawn Player Packet
|
||||||
|
protocol.registerOutgoing(State.PLAY, 0x0C, 0x05, new PacketRemapper() {
|
||||||
|
@Override
|
||||||
|
public void registerMap() {
|
||||||
|
map(Type.VAR_INT); // 0 - Entity ID
|
||||||
|
map(Type.UUID); // 1 - Player UUID
|
||||||
|
|
||||||
|
map(Type.INT, Type.DOUBLE); // 2 - X - Needs to be divide by 32
|
||||||
|
map(Type.INT, Type.DOUBLE); // 3 - Y - Needs to be divide by 32
|
||||||
|
map(Type.INT, Type.DOUBLE); // 4 - Z - Needs to be divide by 32
|
||||||
|
|
||||||
|
map(Type.BYTE); // 5 - Yaw
|
||||||
|
map(Type.BYTE); // 6 - Pitch
|
||||||
|
|
||||||
|
map(Type.SHORT, Type.NOTHING); // Current Item is discontinued
|
||||||
|
|
||||||
|
// TODO Read Metadata
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package us.myles.ViaVersion2.api.protocol1_9to1_8.storage;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import us.myles.ViaVersion2.api.data.StoredObject;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class EntityTracker extends StoredObject{
|
||||||
|
private final Map<Integer, UUID> uuidMap = new HashMap<>();
|
||||||
|
private final Map<Integer, EntityType> clientEntityTypes = new HashMap<>();
|
||||||
|
private final Map<Integer, Integer> vehicleMap = new HashMap<>();
|
||||||
|
|
||||||
|
public UUID getEntityUUID(int id) {
|
||||||
|
if (uuidMap.containsKey(id)) {
|
||||||
|
return uuidMap.get(id);
|
||||||
|
} else {
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
uuidMap.put(id, uuid);
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package us.myles.ViaVersion2.api.remapper;
|
||||||
|
|
||||||
|
import us.myles.ViaVersion2.api.PacketWrapper;
|
||||||
|
import us.myles.ViaVersion2.api.util.Pair;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class PacketRemapper {
|
||||||
|
private List<Pair<ValueReader, ValueTransformer>> valueRemappers = new ArrayList<>();
|
||||||
|
|
||||||
|
public PacketRemapper() {
|
||||||
|
registerMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void map(Type type) {
|
||||||
|
TypeRemapper remapper = new TypeRemapper(type);
|
||||||
|
map(remapper, remapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void map(Type oldType, Type newType) {
|
||||||
|
map(new TypeRemapper(oldType), new TypeRemapper(newType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void map(ValueReader<T> inputRemapper, ValueTransformer<T> outputRemapper) {
|
||||||
|
valueRemappers.add(new Pair<ValueReader, ValueTransformer>(inputRemapper, outputRemapper));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void create(ValueCreator transformer) {
|
||||||
|
map(new TypeRemapper(Type.NOTHING), transformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void registerMap();
|
||||||
|
|
||||||
|
public void remap(PacketWrapper packetWrapper) {
|
||||||
|
// Read all the current values
|
||||||
|
for(Pair<ValueReader, ValueTransformer> valueRemapper : valueRemappers){
|
||||||
|
Object object = valueRemapper.getKey().read(packetWrapper);
|
||||||
|
// Convert object to write type :O!!!
|
||||||
|
// TODO: Data converter lol
|
||||||
|
valueRemapper.getValue().write(packetWrapper, object);
|
||||||
|
}
|
||||||
|
// If we had handlers we'd put them here
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package us.myles.ViaVersion2.api.remapper;
|
||||||
|
|
||||||
|
import us.myles.ViaVersion2.api.PacketWrapper;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class TypeRemapper<T> implements ValueReader<T>, ValueTransformer<T> {
|
||||||
|
private final Type<T> type;
|
||||||
|
|
||||||
|
public TypeRemapper(Type<T> type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T read(PacketWrapper wrapper) {
|
||||||
|
return wrapper.read(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(PacketWrapper output, T inputValue) {
|
||||||
|
output.write(type, inputValue);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package us.myles.ViaVersion2.api.remapper;
|
||||||
|
|
||||||
|
import us.myles.ViaVersion2.api.PacketWrapper;
|
||||||
|
|
||||||
|
public abstract class ValueCreator implements ValueTransformer{
|
||||||
|
public abstract void write(PacketWrapper wrapper);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(PacketWrapper writer, Object inputValue) {
|
||||||
|
write(writer);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package us.myles.ViaVersion2.api.remapper;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.PacketWrapper;
|
||||||
|
|
||||||
|
public interface ValueReader<T> {
|
||||||
|
public T read(PacketWrapper wrapper);
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package us.myles.ViaVersion2.api.remapper;
|
||||||
|
|
||||||
|
import us.myles.ViaVersion2.api.PacketWrapper;
|
||||||
|
|
||||||
|
public interface ValueTransformer<T> {
|
||||||
|
public void write(PacketWrapper writer, T inputValue);
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
public interface ByteBufReader<T> {
|
||||||
|
public T read(ByteBuf buffer);
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
public interface ByteBufWriter<T> {
|
||||||
|
public void write(ByteBuf buffer, T object);
|
||||||
|
}
|
63
src/main/java/us/myles/ViaVersion2/api/type/Type.java
Normal file
63
src/main/java/us/myles/ViaVersion2/api/type/Type.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type;
|
||||||
|
|
||||||
|
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import lombok.Getter;
|
||||||
|
import us.myles.ViaVersion2.api.type.types.*;
|
||||||
|
import us.myles.ViaVersion2.api.util.Position;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
|
||||||
|
/* Defined Types */
|
||||||
|
public static final Type<Byte> BYTE = new ByteType();
|
||||||
|
public static final Type<Byte[]> BYTE_ARRAY = new ArrayType<>(Type.BYTE);
|
||||||
|
|
||||||
|
public static final Type<Short> UNSIGNED_BYTE = new UnsignedByteType();
|
||||||
|
public static final Type<Short[]> UNSIGNED_BYTE_ARRAY = new ArrayType<>(Type.UNSIGNED_BYTE);
|
||||||
|
|
||||||
|
public static final Type<Boolean> BOOLEAN = new BooleanType();
|
||||||
|
public static final Type<Boolean[]> BOOLEAN_ARRAY = new ArrayType<>(Type.BOOLEAN);
|
||||||
|
/* Number Types */
|
||||||
|
public static final Type<Integer> INT = new IntType();
|
||||||
|
public static final Type<Integer[]> INT_ARRAY = new ArrayType<>(Type.INT);
|
||||||
|
|
||||||
|
public static final Type<Double> DOUBLE = new DoubleType();
|
||||||
|
public static final Type<Double[]> DOUBLE_ARRAY = new ArrayType<>(Type.DOUBLE);
|
||||||
|
|
||||||
|
public static final Type<Long> LONG = new LongType();
|
||||||
|
public static final Type<Long[]> LONG_ARRAY = new ArrayType<>(Type.LONG);
|
||||||
|
|
||||||
|
public static final Type<Float> FLOAT = new FloatType();
|
||||||
|
public static final Type<Float[]> FLOAT_ARRAY = new ArrayType<>(Type.FLOAT);
|
||||||
|
|
||||||
|
public static final Type<Short> SHORT = new ShortType();
|
||||||
|
public static final Type<Short[]> SHORT_ARRAY = new ArrayType<>(Type.SHORT);
|
||||||
|
/* Other Types */
|
||||||
|
public static final Type<String> STRING = new StringType();
|
||||||
|
public static final Type<String[]> STRING_ARRAY = new ArrayType<>(Type.STRING);
|
||||||
|
|
||||||
|
public static final Type<UUID> UUID = new UUIDType();
|
||||||
|
public static final Type<UUID[]> UUID_ARRAY = new ArrayType<>(Type.UUID);
|
||||||
|
/* Variable Types */
|
||||||
|
public static final Type<Integer> VAR_INT = new VarIntType();
|
||||||
|
public static final Type<Integer[]> VAR_INT_ARRAY = new ArrayType<>(Type.VAR_INT);
|
||||||
|
/* Special Types */
|
||||||
|
public static final Type<Void> NOTHING = new VoidType(); // This is purely used for remapping.
|
||||||
|
/* MC Types */
|
||||||
|
public static final Type<Position> POSITION = new PositionType(); // This is purely used for remapping.
|
||||||
|
/* Actual Class */
|
||||||
|
|
||||||
|
private final Class<T> outputClass;
|
||||||
|
private final String typeName;
|
||||||
|
|
||||||
|
public Type(Class<T> outputClass) {
|
||||||
|
this(outputClass.getSimpleName(), outputClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type(String typeName, Class<T> outputClass) {
|
||||||
|
this.outputClass = outputClass;
|
||||||
|
this.typeName = typeName;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class ArrayType<T> extends Type<T[]> {
|
||||||
|
private final Type<T> elementType;
|
||||||
|
|
||||||
|
public ArrayType(Type<T> type) {
|
||||||
|
super(type.getTypeName() + " Array", (Class<T[]>) getArrayClass(type.getOutputClass()));
|
||||||
|
this.elementType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T[] read(ByteBuf buffer) {
|
||||||
|
int amount = Type.VAR_INT.read(buffer);
|
||||||
|
Object[] array = new Object[amount];
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
array[i] = elementType.read(buffer);
|
||||||
|
}
|
||||||
|
return (T[]) array;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, T[] object) {
|
||||||
|
Type.VAR_INT.write(buffer, object.length);
|
||||||
|
for (T o : object) {
|
||||||
|
elementType.write(buffer, o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Taken from http://stackoverflow.com/questions/4901128/obtaining-the-array-class-of-a-component-type */
|
||||||
|
public static Class<?> getArrayClass(Class<?> componentType) {
|
||||||
|
ClassLoader classLoader = componentType.getClassLoader();
|
||||||
|
String name;
|
||||||
|
if (componentType.isArray()) {
|
||||||
|
// just add a leading "["
|
||||||
|
name = "[" + componentType.getName();
|
||||||
|
} else if (componentType == boolean.class) {
|
||||||
|
name = "[Z";
|
||||||
|
} else if (componentType == byte.class) {
|
||||||
|
name = "[B";
|
||||||
|
} else if (componentType == char.class) {
|
||||||
|
name = "[C";
|
||||||
|
} else if (componentType == double.class) {
|
||||||
|
name = "[D";
|
||||||
|
} else if (componentType == float.class) {
|
||||||
|
name = "[F";
|
||||||
|
} else if (componentType == int.class) {
|
||||||
|
name = "[I";
|
||||||
|
} else if (componentType == long.class) {
|
||||||
|
name = "[J";
|
||||||
|
} else if (componentType == short.class) {
|
||||||
|
name = "[S";
|
||||||
|
} else {
|
||||||
|
// must be an object non-array class
|
||||||
|
name = "[L" + componentType.getName() + ";";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return classLoader != null ? classLoader.loadClass(name) : Class.forName(name);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return null; // oh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class BooleanType extends Type<Boolean> {
|
||||||
|
public BooleanType() {
|
||||||
|
super(Boolean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean read(ByteBuf buffer) {
|
||||||
|
return buffer.readBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, Boolean object) {
|
||||||
|
buffer.writeBoolean(object);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class ByteType extends Type<Byte> {
|
||||||
|
public ByteType() {
|
||||||
|
super(Byte.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Byte read(ByteBuf buffer) {
|
||||||
|
return buffer.readByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, Byte object) {
|
||||||
|
buffer.writeByte(object);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class DoubleType extends Type<Double> {
|
||||||
|
public DoubleType() {
|
||||||
|
super(Double.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double read(ByteBuf buffer) {
|
||||||
|
return buffer.readDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, Double object) {
|
||||||
|
buffer.writeDouble(object);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class FloatType extends Type<Float> {
|
||||||
|
public FloatType() {
|
||||||
|
super(Float.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Float read(ByteBuf buffer) {
|
||||||
|
return buffer.readFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, Float object) {
|
||||||
|
buffer.writeFloat(object);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class IntType extends Type<Integer> {
|
||||||
|
public IntType() {
|
||||||
|
super(Integer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer read(ByteBuf buffer) {
|
||||||
|
return buffer.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, Integer object) {
|
||||||
|
buffer.writeInt(object);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class LongType extends Type<Long> {
|
||||||
|
public LongType() {
|
||||||
|
super(Long.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long read(ByteBuf buffer) {
|
||||||
|
return buffer.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, Long object) {
|
||||||
|
buffer.writeLong(object);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
import us.myles.ViaVersion2.api.util.Position;
|
||||||
|
|
||||||
|
public class PositionType extends Type<Position> {
|
||||||
|
public PositionType() {
|
||||||
|
super(Position.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Position read(ByteBuf buffer) {
|
||||||
|
long val = buffer.readLong();
|
||||||
|
long x = (val >> 38); // signed
|
||||||
|
long y = (val >> 26) & 0xfff; // unsigned
|
||||||
|
// this shifting madness is used to preserve sign
|
||||||
|
long z = (val << 38) >> 38; // signed
|
||||||
|
|
||||||
|
return new Position(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, Position object) {
|
||||||
|
buffer.writeLong(((object.getX() & 0x3ffffff) << 38) | ((object.getY() & 0xfff) << 26) | (object.getZ() & 0x3ffffff));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class ShortType extends Type<Short> {
|
||||||
|
public ShortType() {
|
||||||
|
super(Short.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Short read(ByteBuf buffer) {
|
||||||
|
return buffer.readShort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, Short object) {
|
||||||
|
buffer.writeShort(object);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class StringType extends Type<String> {
|
||||||
|
public StringType() {
|
||||||
|
super(String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String read(ByteBuf buffer) {
|
||||||
|
int len = Type.VAR_INT.read(buffer);
|
||||||
|
Preconditions.checkArgument(len <= Short.MAX_VALUE, "Cannot receive string longer than Short.MAX_VALUE (got %s characters)", len);
|
||||||
|
|
||||||
|
byte[] b = new byte[len];
|
||||||
|
buffer.readBytes(b);
|
||||||
|
|
||||||
|
return new String(b, Charsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, String object) {
|
||||||
|
Preconditions.checkArgument(object.length() <= Short.MAX_VALUE, "Cannot send string longer than Short.MAX_VALUE (got %s characters)", object.length());
|
||||||
|
|
||||||
|
byte[] b = object.getBytes(Charsets.UTF_8);
|
||||||
|
Type.VAR_INT.write(buffer, b.length);
|
||||||
|
buffer.writeBytes(b);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class UUIDType extends Type<UUID> {
|
||||||
|
public UUIDType() {
|
||||||
|
super(UUID.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID read(ByteBuf buffer) {
|
||||||
|
return new UUID(buffer.readLong(), buffer.readLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, UUID object) {
|
||||||
|
buffer.writeLong(object.getMostSignificantBits());
|
||||||
|
buffer.writeLong(object.getLeastSignificantBits());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class UnsignedByteType extends Type<Short> {
|
||||||
|
public UnsignedByteType() {
|
||||||
|
super(Short.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Short read(ByteBuf buffer) {
|
||||||
|
return buffer.readUnsignedByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, Short object) {
|
||||||
|
buffer.writeByte(object);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class VarIntType extends Type<Integer>{
|
||||||
|
|
||||||
|
public VarIntType() {
|
||||||
|
super("VarInt", Integer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, Integer object) {
|
||||||
|
int part;
|
||||||
|
while (true) {
|
||||||
|
part = object & 0x7F;
|
||||||
|
|
||||||
|
object >>>= 7;
|
||||||
|
if (object != 0) {
|
||||||
|
part |= 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.writeByte(part);
|
||||||
|
|
||||||
|
if (object == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer read(ByteBuf buffer) {
|
||||||
|
int out = 0;
|
||||||
|
int bytes = 0;
|
||||||
|
byte in;
|
||||||
|
while (true) {
|
||||||
|
in = buffer.readByte();
|
||||||
|
|
||||||
|
out |= (in & 0x7F) << (bytes++ * 7);
|
||||||
|
|
||||||
|
if (bytes > 5) { // 5 is maxBytes
|
||||||
|
throw new RuntimeException("VarInt too big");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((in & 0x80) != 0x80) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package us.myles.ViaVersion2.api.type.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion2.api.type.Type;
|
||||||
|
|
||||||
|
public class VoidType extends Type<Void> {
|
||||||
|
public VoidType() {
|
||||||
|
super(Void.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void read(ByteBuf buffer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, Void object) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
16
src/main/java/us/myles/ViaVersion2/api/util/Pair.java
Normal file
16
src/main/java/us/myles/ViaVersion2/api/util/Pair.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package us.myles.ViaVersion2.api.util;
|
||||||
|
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class Pair<X, Y> {
|
||||||
|
private final X key;
|
||||||
|
private final Y value;
|
||||||
|
|
||||||
|
public Pair(X key, Y value){
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
12
src/main/java/us/myles/ViaVersion2/api/util/Position.java
Normal file
12
src/main/java/us/myles/ViaVersion2/api/util/Position.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package us.myles.ViaVersion2.api.util;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class Position {
|
||||||
|
private Long x;
|
||||||
|
private Long y;
|
||||||
|
private Long z;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user