-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
132 additions
and
240 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 58 additions & 41 deletions
99
common/src/main/java/cn/zbx1425/worldcomment/data/CommentEntry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,73 +1,90 @@ | ||
package cn.zbx1425.worldcomment.data; | ||
|
||
import io.netty.buffer.Unpooled; | ||
import net.minecraft.core.BlockPos; | ||
import net.minecraft.core.UUIDUtil; | ||
import net.minecraft.network.FriendlyByteBuf; | ||
import net.minecraft.resources.ResourceLocation; | ||
import net.minecraft.world.entity.player.Player; | ||
import net.minecraft.world.level.ChunkPos; | ||
|
||
import java.io.File; | ||
import java.io.FileNotFoundException; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.nio.ByteBuffer; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.util.UUID; | ||
|
||
public class CommentEntry { | ||
|
||
public static int REGION_SHIFT = 2; | ||
|
||
public long id; | ||
public long timestamp; | ||
public boolean deleted; | ||
public ResourceLocation level; | ||
public ChunkPos region; | ||
public BlockPos location; | ||
public UUID initiator; | ||
public String initiatorName; | ||
public int messageType; | ||
public String message; | ||
public boolean deleted; | ||
public String imageUrl; | ||
|
||
public CommentEntry(CommentTable table, ResultSet result) throws SQLException { | ||
int iota = 0; | ||
id = result.getLong(++iota); | ||
timestamp = result.getLong(++iota); | ||
deleted = result.getBoolean(++iota); | ||
level = table.db.dimensions.getDimensionById(result.getInt(++iota)); | ||
region = new ChunkPos(result.getLong(++iota)); | ||
location = new BlockPos(result.getInt(++iota), result.getInt(++iota), result.getInt(++iota)); | ||
initiator = uuidFromByteArray(result.getBytes(++iota)); | ||
initiatorName = result.getString(++iota); | ||
messageType = result.getInt(++iota); | ||
message = result.getString(++iota); | ||
imageUrl = result.getString(++iota); | ||
public long fileOffset; | ||
|
||
public CommentEntry(Player initiator, BlockPos placedAt, int messageType, String message, String imageUrl) { | ||
id = Database.SNOWFLAKE.nextId(); | ||
timestamp = System.currentTimeMillis(); | ||
level = initiator.level().dimension().location(); | ||
location = placedAt; | ||
region = new ChunkPos(location.getX() >> REGION_SHIFT, location.getZ() >> REGION_SHIFT); | ||
this.initiator = initiator.getGameProfile().getId(); | ||
initiatorName = initiator.getGameProfile().getName(); | ||
this.messageType = messageType; | ||
this.message = message; | ||
deleted = false; | ||
this.imageUrl = imageUrl; | ||
} | ||
|
||
public CommentEntry(ResourceLocation level, FriendlyByteBuf src) { | ||
fileOffset = src.readerIndex(); | ||
id = src.readLong(); | ||
timestamp = src.readLong(); | ||
this.level = level; | ||
location = src.readBlockPos(); | ||
region = new ChunkPos(location.getX() >> REGION_SHIFT, location.getZ() >> REGION_SHIFT); | ||
initiator = src.readUUID(); | ||
initiatorName = src.readUtf(); | ||
messageType = src.readInt(); | ||
message = src.readUtf(); | ||
deleted = src.readBoolean(); | ||
imageUrl = src.readUtf(); | ||
src.skipBytes(16 - (src.readerIndex() % 16)); | ||
} | ||
|
||
private static UUID uuidFromByteArray(byte[] bytes) { | ||
ByteBuffer bb = ByteBuffer.wrap(bytes); | ||
return new UUID(bb.getLong(), bb.getLong()); | ||
} | ||
|
||
public void insertTo(CommentTable table) throws SQLException { | ||
table.db.execute(""" | ||
INSERT OR REPLACE INTO comments ( | ||
id, timestamp, deleted, level, region, locationX, locationY, locationZ, initiator, initiatorName, messageType, message, imageUrl | ||
) VALUES ( | ||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? | ||
); | ||
""", params -> { | ||
int iota = 0; | ||
if (id == 0) id = Database.SNOWFLAKE.nextId(); | ||
params.setLong(++iota, id); | ||
params.setLong(++iota, timestamp); | ||
params.setBoolean(++iota, deleted); | ||
params.setInt(++iota, table.db.dimensions.getDimensionId(level)); | ||
params.setLong(++iota, region.toLong()); | ||
params.setInt(++iota, location.getX()); | ||
params.setInt(++iota, location.getY()); | ||
params.setInt(++iota, location.getZ()); | ||
params.setBytes(++iota, UUIDUtil.uuidToByteArray(initiator)); | ||
params.setString(++iota, initiatorName); | ||
params.setInt(++iota, messageType); | ||
params.setString(++iota, message); | ||
params.setString(++iota, imageUrl); | ||
}); | ||
public void writeBuffer(FriendlyByteBuf dst) { | ||
dst.writeLong(id); | ||
dst.writeLong(timestamp); | ||
dst.writeBlockPos(location); | ||
dst.writeUUID(initiator); | ||
dst.writeUtf(initiatorName); | ||
dst.writeInt(messageType); | ||
dst.writeUtf(message); | ||
dst.writeBoolean(deleted); | ||
dst.writeUtf(imageUrl); | ||
dst.writeZero(16 - (dst.writerIndex() % 16)); | ||
} | ||
|
||
public void writeFileStream(FileOutputStream oStream) throws IOException { | ||
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(256)); | ||
writeBuffer(buf); | ||
fileOffset = oStream.getChannel().position(); | ||
oStream.write(buf.array(), 0, buf.writerIndex()); | ||
} | ||
|
||
} |
111 changes: 59 additions & 52 deletions
111
common/src/main/java/cn/zbx1425/worldcomment/data/CommentTable.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,82 @@ | ||
package cn.zbx1425.worldcomment.data; | ||
|
||
import io.netty.buffer.Unpooled; | ||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap; | ||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; | ||
import net.minecraft.network.FriendlyByteBuf; | ||
import net.minecraft.resources.ResourceLocation; | ||
import net.minecraft.world.level.ChunkPos; | ||
import net.minecraft.world.level.Level; | ||
|
||
import java.sql.ResultSet; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.sql.SQLException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.*; | ||
import java.util.stream.Stream; | ||
|
||
public class CommentTable { | ||
|
||
public final Database db; | ||
public int maxId; | ||
|
||
Map<ResourceLocation, Long2ObjectMap<List<CommentEntry>>> regionIndex = new HashMap<>(); | ||
Map<UUID, List<CommentEntry>> playerIndex = new HashMap<>(); | ||
|
||
public CommentTable(Database db) { | ||
this.db = db; | ||
} | ||
|
||
public void init() throws SQLException { | ||
db.execute(""" | ||
CREATE TABLE IF NOT EXISTS comments ( | ||
id INTEGER PRIMARY KEY, | ||
timestamp INTEGER, | ||
deleted INTEGER, | ||
level INTEGER, | ||
region INTEGER, | ||
locationX INTEGER, | ||
locationY INTEGER, | ||
locationZ INTEGER, | ||
initiator BLOB, | ||
initiatorName TEXT, | ||
messageType INTEGER, | ||
message TEXT, | ||
imageUrl TEXT | ||
); | ||
CREATE INDEX IF NOT EXISTS regionIndex ON comments ( | ||
level, | ||
region | ||
); | ||
CREATE INDEX IF NOT EXISTS timestampIndex ON comments ( | ||
timestamp | ||
); | ||
"""); | ||
} | ||
public void load() throws IOException { | ||
Files.createDirectories(db.basePath.resolve("regions")); | ||
for (Level level : db.server.getAllLevels()) { | ||
ResourceLocation dimension = level.dimension().location(); | ||
Path levelPath = getLevelPath(dimension); | ||
Files.createDirectory(levelPath); | ||
regionIndex.put(dimension, new Long2ObjectOpenHashMap<>()); | ||
try (Stream<Path> files = Files.list(levelPath)) { | ||
for (Path file : files.toList()) { | ||
long region = Long.parseUnsignedLong(file.getFileName().toString().substring(1, 9), 16); | ||
List<CommentEntry> regionEntries = new ArrayList<>(); | ||
regionIndex.get(dimension).put(region, regionEntries); | ||
|
||
public List<CommentEntry> queryInRegion(ResourceLocation level, ChunkPos region) throws SQLException { | ||
ArrayList<CommentEntry> entries = new ArrayList<>(); | ||
try (ResultSet result = db.executeQuery( | ||
"SELECT * FROM comments WHERE level = ? AND region = ?", params -> { | ||
params.setInt(1, db.dimensions.getDimensionId(level)); | ||
params.setLong(2, region.toLong()); | ||
} | ||
)) { | ||
while (result.next()) { | ||
entries.add(new CommentEntry(this, result)); | ||
byte[] fileContent = Files.readAllBytes(file); | ||
FriendlyByteBuf src = new FriendlyByteBuf(Unpooled.wrappedBuffer(fileContent)); | ||
while (src.readerIndex() < fileContent.length - 1) { | ||
CommentEntry entry = new CommentEntry(dimension, src); | ||
regionEntries.add(entry); | ||
playerIndex.computeIfAbsent(entry.initiator, ignored -> new ArrayList<>()) | ||
.add(entry); | ||
} | ||
} | ||
} | ||
} | ||
return entries; | ||
} | ||
|
||
public List<CommentEntry> queryInTime(long from) throws SQLException { | ||
ArrayList<CommentEntry> entries = new ArrayList<>(); | ||
try (ResultSet result = db.executeQuery( | ||
"SELECT * FROM comments WHERE timestamp > ?", params -> { | ||
params.setLong(1, from); | ||
} | ||
)) { | ||
while (result.next()) { | ||
entries.add(new CommentEntry(this, result)); | ||
} | ||
private Path getLevelPath(ResourceLocation dimension) { | ||
return db.basePath.resolve("regions") | ||
.resolve(dimension.getNamespace() + "+" + dimension.getPath()); | ||
} | ||
|
||
private Path getLevelRegionPath(ResourceLocation dimension, ChunkPos region) { | ||
return db.basePath.resolve("regions") | ||
.resolve(dimension.getNamespace() + "+" + dimension.getPath()) | ||
.resolve("r" + Long.toHexString(region.toLong()) + ".bin"); | ||
} | ||
|
||
public List<CommentEntry> queryInRegion(ResourceLocation level, ChunkPos region) throws SQLException { | ||
return regionIndex.get(level).get(region.toLong()); | ||
} | ||
|
||
public void insert(CommentEntry newEntry) throws IOException { | ||
Path targetFile = getLevelRegionPath(newEntry.level, newEntry.region); | ||
try (FileOutputStream oStream = new FileOutputStream(targetFile.toFile(), true)) { | ||
newEntry.writeFileStream(oStream); | ||
} | ||
return entries; | ||
regionIndex.get(newEntry.level) | ||
.computeIfAbsent(newEntry.region.toLong(), ignored -> new ArrayList<>()) | ||
.add(newEntry); | ||
playerIndex.computeIfAbsent(newEntry.initiator, ignored -> new ArrayList<>()) | ||
.add(newEntry); | ||
} | ||
} |
Oops, something went wrong.