mirror of
https://github.com/Nan1t/NanoLimbo.git
synced 2025-07-14 21:20:15 +02:00
Optimized packet snapshot. Readers closing. Restructured schematic classes
This commit is contained in:
parent
864a84b9b6
commit
1c3cb9b77d
@ -160,9 +160,7 @@ public class ByteMessage extends ByteBuf {
|
||||
}
|
||||
|
||||
public void writeCompoundTagArray(CompoundBinaryTag[] compoundTags) {
|
||||
try {
|
||||
ByteBufOutputStream stream = new ByteBufOutputStream(buf);
|
||||
|
||||
try (ByteBufOutputStream stream = new ByteBufOutputStream(buf)) {
|
||||
writeVarInt(compoundTags.length);
|
||||
|
||||
for (CompoundBinaryTag tag : compoundTags) {
|
||||
@ -174,16 +172,16 @@ public class ByteMessage extends ByteBuf {
|
||||
}
|
||||
|
||||
public CompoundBinaryTag readCompoundTag() {
|
||||
try {
|
||||
return BinaryTagIO.reader().read((InputStream) new ByteBufInputStream(buf));
|
||||
try (ByteBufInputStream stream = new ByteBufInputStream(buf)) {
|
||||
return BinaryTagIO.reader().read((InputStream) stream);
|
||||
} catch (IOException thrown) {
|
||||
throw new DecoderException("Cannot read NBT CompoundTag");
|
||||
}
|
||||
}
|
||||
|
||||
public void writeCompoundTag(CompoundBinaryTag compoundTag) {
|
||||
try {
|
||||
BinaryTagIO.writer().write(compoundTag, (OutputStream) new ByteBufOutputStream(buf));
|
||||
try (ByteBufOutputStream stream = new ByteBufOutputStream(buf)) {
|
||||
BinaryTagIO.writer().write(compoundTag, (OutputStream) stream);
|
||||
} catch (IOException e) {
|
||||
throw new EncoderException("Cannot write NBT CompoundTag");
|
||||
}
|
||||
|
@ -22,14 +22,19 @@ import ru.nanit.limbo.protocol.registry.Version;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* PacketSnapshot encodes packet to byt array for each MC version.
|
||||
* Some versions have same bytes snapshot, so there are mappings
|
||||
* to avoid storing same byte array for different versions
|
||||
*/
|
||||
public class PacketSnapshot implements PacketOut {
|
||||
|
||||
private final PacketOut packet;
|
||||
private final Map<Version, byte[]> versionMessages;
|
||||
private final Map<Version, byte[]> versionMessages = new HashMap<>();
|
||||
private final Map<Version, Version> mappings = new HashMap<>();
|
||||
|
||||
public PacketSnapshot(PacketOut packet) {
|
||||
this.packet = packet;
|
||||
this.versionMessages = new HashMap<>();
|
||||
}
|
||||
|
||||
public PacketOut getWrappedPacket() {
|
||||
@ -37,11 +42,24 @@ public class PacketSnapshot implements PacketOut {
|
||||
}
|
||||
|
||||
public PacketSnapshot encodePacket() {
|
||||
Map<Integer, Version> hashes = new HashMap<>();
|
||||
|
||||
for (Version version : Version.values()) {
|
||||
if (version.equals(Version.UNDEFINED)) continue;
|
||||
|
||||
ByteMessage encodedMessage = ByteMessage.create();
|
||||
packet.encode(encodedMessage, version);
|
||||
byte[] message = encodedMessage.toByteArray();
|
||||
versionMessages.put(version, message);
|
||||
|
||||
int hash = encodedMessage.hashCode();
|
||||
Version hashed = hashes.get(hash);
|
||||
|
||||
if (hashed != null) {
|
||||
mappings.put(version, hashed);
|
||||
continue;
|
||||
}
|
||||
|
||||
hashes.put(hash, version);
|
||||
versionMessages.put(version, encodedMessage.toByteArray());
|
||||
encodedMessage.release();
|
||||
}
|
||||
|
||||
@ -50,11 +68,11 @@ public class PacketSnapshot implements PacketOut {
|
||||
|
||||
@Override
|
||||
public void encode(ByteMessage msg, Version version) {
|
||||
byte[] message = versionMessages.get(version);
|
||||
Version mapped = mappings.get(version);
|
||||
byte[] message = versionMessages.get(mapped);
|
||||
|
||||
if (message != null) {
|
||||
if (message != null)
|
||||
msg.writeBytes(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Nan1t
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ru.nanit.limbo.world;
|
||||
|
||||
import ru.nanit.limbo.protocol.registry.Version;
|
||||
|
||||
public final class BlockMap {
|
||||
|
||||
public BlockData convert(int id, byte data, Version version) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
public BlockData convert(String state, Version version) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
68
src/main/java/ru/nanit/limbo/world/BlockMappings.java
Normal file
68
src/main/java/ru/nanit/limbo/world/BlockMappings.java
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Nan1t
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ru.nanit.limbo.world;
|
||||
|
||||
import ru.nanit.limbo.protocol.registry.Version;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class BlockMappings {
|
||||
|
||||
private final Map<String, String> idToState = new HashMap<>();
|
||||
private final Map<String, String> stateToId = new HashMap<>();
|
||||
|
||||
public BlockData convert(int id, byte data, Version version) {
|
||||
if (version.less(Version.V1_13))
|
||||
return new BlockData(id, data);
|
||||
|
||||
String state = idToState.get(toId(id, data));
|
||||
|
||||
return state != null ? new BlockData(state) : null;
|
||||
}
|
||||
|
||||
public BlockData convert(String state, Version version) {
|
||||
if (state == null) return null;
|
||||
|
||||
if (version.moreOrEqual(Version.V1_13)) {
|
||||
return new BlockData(state);
|
||||
}
|
||||
|
||||
String id = stateToId.get(state);
|
||||
|
||||
if (id != null) {
|
||||
String[] arr = id.split(":");
|
||||
int blockId = Integer.parseInt(arr[0]);
|
||||
byte data = Byte.parseByte(arr[1]);
|
||||
|
||||
return new BlockData(blockId, data);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void register(int id, byte data, String state) {
|
||||
String strId = toId(id, data);
|
||||
idToState.put(strId, state);
|
||||
stateToId.put(state, strId);
|
||||
}
|
||||
|
||||
private String toId(int id, byte data) {
|
||||
return id + ":" + data;
|
||||
}
|
||||
}
|
@ -89,9 +89,15 @@ public final class DimensionRegistry {
|
||||
return TagStringIO.get().asCompound(streamToString(in));
|
||||
}
|
||||
|
||||
private String streamToString(InputStream in) {
|
||||
return new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
|
||||
.lines()
|
||||
private String streamToString(InputStream in) throws IOException {
|
||||
InputStreamReader isReader = new InputStreamReader(in, StandardCharsets.UTF_8);
|
||||
BufferedReader bufReader = new BufferedReader(isReader);
|
||||
String content = bufReader.lines()
|
||||
.collect(Collectors.joining("\n"));
|
||||
|
||||
isReader.close();
|
||||
bufReader.close();
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Nan1t
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ru.nanit.limbo.world.schematic;
|
||||
|
||||
import ru.nanit.limbo.world.BlockEntity;
|
||||
import ru.nanit.limbo.world.BlockMappings;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractSchematic implements Schematic {
|
||||
|
||||
protected final BlockMappings mappings;
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
private int length;
|
||||
private List<BlockEntity> blockEntities;
|
||||
|
||||
public AbstractSchematic(BlockMappings mappings) {
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BlockEntity> getBlockEntities() {
|
||||
return blockEntities;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void setLength(int length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public void setBlockEntities(List<BlockEntity> blockEntities) {
|
||||
this.blockEntities = blockEntities;
|
||||
}
|
||||
}
|
@ -20,7 +20,6 @@ package ru.nanit.limbo.world.schematic;
|
||||
import ru.nanit.limbo.protocol.registry.Version;
|
||||
import ru.nanit.limbo.world.BlockData;
|
||||
import ru.nanit.limbo.world.BlockEntity;
|
||||
import ru.nanit.limbo.world.BlockMap;
|
||||
import ru.nanit.limbo.world.Location;
|
||||
|
||||
import java.util.List;
|
||||
@ -35,6 +34,6 @@ public interface Schematic {
|
||||
|
||||
List<BlockEntity> getBlockEntities();
|
||||
|
||||
BlockData getBlock(Location loc, Version version, BlockMap mappings);
|
||||
BlockData getBlock(Location loc, Version version);
|
||||
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import net.kyori.adventure.nbt.BinaryTag;
|
||||
import net.kyori.adventure.nbt.BinaryTagIO;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import ru.nanit.limbo.world.BlockEntity;
|
||||
import ru.nanit.limbo.world.BlockMappings;
|
||||
import ru.nanit.limbo.world.Location;
|
||||
import ru.nanit.limbo.world.schematic.versions.LegacySchematic;
|
||||
import ru.nanit.limbo.world.schematic.versions.SpongeSchematic;
|
||||
@ -36,6 +37,12 @@ import java.util.Map;
|
||||
|
||||
public class SchematicLoader {
|
||||
|
||||
private final BlockMappings mappings;
|
||||
|
||||
public SchematicLoader(BlockMappings mappings) {
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
public Schematic load(Path file) throws IOException {
|
||||
return load(Files.newInputStream(file));
|
||||
}
|
||||
@ -43,16 +50,16 @@ public class SchematicLoader {
|
||||
public Schematic load(InputStream stream) throws IOException {
|
||||
CompoundBinaryTag nbt = BinaryTagIO.unlimitedReader().read(stream, BinaryTagIO.Compression.GZIP);
|
||||
|
||||
if (nbt.getCompound("BlockData") == CompoundBinaryTag.empty()) {
|
||||
return loadLegacy(nbt);
|
||||
} else {
|
||||
if (nbt.keySet().contains("BlockData")) {
|
||||
return loadSponge(nbt);
|
||||
} else {
|
||||
return loadLegacy(nbt);
|
||||
}
|
||||
}
|
||||
|
||||
// Specification: https://github.com/SpongePowered/Schematic-Specification/blob/master/versions/schematic-2.md
|
||||
private Schematic loadSponge(CompoundBinaryTag nbt) {
|
||||
SpongeSchematic schematic = new SpongeSchematic();
|
||||
SpongeSchematic schematic = new SpongeSchematic(mappings);
|
||||
|
||||
schematic.setDataVersion(nbt.getInt("DataVersion"));
|
||||
|
||||
@ -93,7 +100,7 @@ public class SchematicLoader {
|
||||
}
|
||||
|
||||
private Schematic loadLegacy(CompoundBinaryTag nbt) {
|
||||
LegacySchematic schematic = new LegacySchematic();
|
||||
LegacySchematic schematic = new LegacySchematic(mappings);
|
||||
|
||||
schematic.setWidth(nbt.getShort("Width"));
|
||||
schematic.setHeight(nbt.getShort("Height"));
|
||||
|
@ -19,68 +19,33 @@ package ru.nanit.limbo.world.schematic.versions;
|
||||
|
||||
import ru.nanit.limbo.protocol.registry.Version;
|
||||
import ru.nanit.limbo.world.BlockData;
|
||||
import ru.nanit.limbo.world.BlockEntity;
|
||||
import ru.nanit.limbo.world.BlockMap;
|
||||
import ru.nanit.limbo.world.BlockMappings;
|
||||
import ru.nanit.limbo.world.Location;
|
||||
import ru.nanit.limbo.world.schematic.Schematic;
|
||||
|
||||
import java.util.List;
|
||||
import ru.nanit.limbo.world.schematic.AbstractSchematic;
|
||||
|
||||
/**
|
||||
* Legacy schematic format (1.12-)
|
||||
*/
|
||||
public class LegacySchematic implements Schematic {
|
||||
public class LegacySchematic extends AbstractSchematic {
|
||||
|
||||
private short width;
|
||||
private short height;
|
||||
private short length;
|
||||
private Materials materials;
|
||||
private byte[] blocks;
|
||||
private byte[] addBlocks;
|
||||
private byte[] data;
|
||||
private List<BlockEntity> blockEntities;
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
public LegacySchematic(BlockMappings mappings) {
|
||||
super(mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BlockEntity> getBlockEntities() {
|
||||
return blockEntities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData getBlock(Location loc, Version version, BlockMap mappings) {
|
||||
int index = (loc.getBlockY() * length + loc.getBlockZ()) * width + loc.getBlockX();
|
||||
public BlockData getBlock(Location loc, Version version) {
|
||||
int index = (loc.getBlockY() * getLength() + loc.getBlockZ()) * getWidth() + loc.getBlockX();
|
||||
byte id = this.blocks[index];
|
||||
byte data = this.data[index];
|
||||
|
||||
return mappings.convert(id, data, version);
|
||||
}
|
||||
|
||||
public void setWidth(short width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public void setHeight(short height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void setLength(short length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public void setMaterials(Materials materials) {
|
||||
this.materials = materials;
|
||||
}
|
||||
@ -97,21 +62,17 @@ public class LegacySchematic implements Schematic {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public void setBlockEntities(List<BlockEntity> blockEntities) {
|
||||
this.blockEntities = blockEntities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Schematic{" +
|
||||
"width=" + width +
|
||||
", height=" + height +
|
||||
", length=" + length +
|
||||
"width=" + getWidth() +
|
||||
", height=" + getHeight() +
|
||||
", length=" + getLength() +
|
||||
", materials=" + materials +
|
||||
", blocks length=" + blocks.length +
|
||||
", addBlocks length=" + addBlocks.length +
|
||||
", data length=" + data.length +
|
||||
", blockEntities=" + blockEntities +
|
||||
", blockEntities=" + getBlockEntities() +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
@ -19,51 +19,29 @@ package ru.nanit.limbo.world.schematic.versions;
|
||||
|
||||
import ru.nanit.limbo.protocol.registry.Version;
|
||||
import ru.nanit.limbo.world.BlockData;
|
||||
import ru.nanit.limbo.world.BlockEntity;
|
||||
import ru.nanit.limbo.world.BlockMap;
|
||||
import ru.nanit.limbo.world.BlockMappings;
|
||||
import ru.nanit.limbo.world.Location;
|
||||
import ru.nanit.limbo.world.schematic.Schematic;
|
||||
import ru.nanit.limbo.world.schematic.AbstractSchematic;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Modern schematic format (Sponge second specification)
|
||||
*/
|
||||
public class SpongeSchematic implements Schematic {
|
||||
public class SpongeSchematic extends AbstractSchematic {
|
||||
|
||||
private int dataVersion;
|
||||
private int width;
|
||||
private int height;
|
||||
private int length;
|
||||
private int paletteMax;
|
||||
private Map<Integer, String> palette;
|
||||
private byte[] blockData;
|
||||
private List<BlockEntity> blockEntities;
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
public SpongeSchematic(BlockMappings mappings) {
|
||||
super(mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BlockEntity> getBlockEntities() {
|
||||
return blockEntities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData getBlock(Location loc, Version version, BlockMap mappings) {
|
||||
int index = loc.getBlockX() + loc.getBlockZ() * width + loc.getBlockY() * width * length;
|
||||
public BlockData getBlock(Location loc, Version version) {
|
||||
int index = loc.getBlockX() + loc.getBlockZ() * getWidth() + loc.getBlockY() * getWidth() * getLength();
|
||||
int id = blockData[index];
|
||||
String state = palette.get(id);
|
||||
return mappings.convert(state, version);
|
||||
@ -73,18 +51,6 @@ public class SpongeSchematic implements Schematic {
|
||||
this.dataVersion = dataVersion;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void setLength(int length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public void setPaletteMax(int paletteMax) {
|
||||
this.paletteMax = paletteMax;
|
||||
}
|
||||
@ -97,7 +63,17 @@ public class SpongeSchematic implements Schematic {
|
||||
this.blockData = blockData;
|
||||
}
|
||||
|
||||
public void setBlockEntities(List<BlockEntity> blockEntities) {
|
||||
this.blockEntities = blockEntities;
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SpongeSchematic{" +
|
||||
"dataVersion=" + dataVersion +
|
||||
", width=" + getWidth() +
|
||||
", height=" + getHeight() +
|
||||
", length=" + getLength() +
|
||||
", paletteMax=" + paletteMax +
|
||||
", palette=" + palette +
|
||||
", blockData bytes=" + blockData.length +
|
||||
", blockEntities=" + getBlockEntities() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ru.nanit.limbo.world.BlockMappings;
|
||||
import ru.nanit.limbo.world.schematic.SchematicLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -25,10 +26,10 @@ public class SchematicTest {
|
||||
|
||||
@Test
|
||||
public void testLoading() throws IOException {
|
||||
SchematicLoader loader = new SchematicLoader();
|
||||
InputStream stream = getClass().getResourceAsStream("test.schematic");
|
||||
BlockMappings mappings = new BlockMappings();
|
||||
SchematicLoader loader = new SchematicLoader(mappings);
|
||||
InputStream stream = getClass().getResourceAsStream("spawn.schem");
|
||||
|
||||
System.out.println(loader.load(stream));
|
||||
}
|
||||
|
||||
}
|
||||
|
BIN
src/test/resources/spawn.schem
Normal file
BIN
src/test/resources/spawn.schem
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user