Merge pull request 'Add Schematic Revisions' (#93) from schematic-revisions into main

Reviewed-on: SteamWar/SteamWar#93
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
This commit is contained in:
2025-07-14 09:13:16 +02:00
22 changed files with 229 additions and 97 deletions
@@ -23,8 +23,13 @@ import de.steamwar.sql.internal.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import javax.swing.plaf.nimbus.State;
import java.io.*;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.zip.GZIPInputStream;
@AllArgsConstructor
@@ -40,26 +45,47 @@ public class NodeData {
private static final Table<NodeData> table = new Table<>(NodeData.class);
private static final Statement updateDatabase = new Statement("INSERT INTO NodeData(NodeId, NodeFormat, SchemData) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE NodeFormat = VALUES(NodeFormat), SchemData = VALUES(SchemData)");
private static final Statement selSchemData = new Statement("SELECT SchemData FROM NodeData WHERE NodeId = ?");
private static final Statement updateDatabase = new Statement("INSERT INTO NodeData(NodeId, NodeFormat, SchemData) VALUES (?, ?, ?)", true);
private static final Statement selSchemData = new Statement("SELECT SchemData FROM NodeData WHERE NodeId = ? AND CreatedAt = ?");
private static final Statement delete = table.delete(Table.PRIMARY);
private static final SelectStatement<NodeData> get = table.select(Table.PRIMARY);
private static final SelectStatement<NodeData> get = new SelectStatement<>(table, "SELECT NodeId, CreatedAt, NodeFormat FROM NodeData WHERE NodeId = ? ORDER BY CreatedAt ");
private static final Statement getRevisions = new Statement("SELECT COUNT(DISTINCT CreatedAt) as CNT FROM NodeData WHERE NodeId = ?");
private static final SelectStatement<NodeData> getLatest = new SelectStatement<>(table, "SELECT NodeId, CreatedAt, NodeFormat FROM NodeData WHERE NodeId = ? ORDER BY CreatedAt LIMIT 1");
public static NodeData get(SchematicNode node) {
if(node.isDir())
throw new IllegalArgumentException("Node is a directory");
return get.select(rs -> {
if(rs.next()) {
return new NodeData(node.getId(), SchematicFormat.values()[rs.getInt("NodeFormat")]);
public static NodeData getLatest(SchematicNode node) {
if (node.isDir()) throw new IllegalArgumentException("Node is dir");
return Optional.ofNullable(getLatest.select(node)).orElseGet(() -> new NodeData(node.getId(), Timestamp.from(Instant.now()), SchematicFormat.MCEDIT));
}
public static List<NodeData> get(SchematicNode node) {
return get.listSelect(node);
}
public static NodeData get(SchematicNode node, int revision) {
return get.listSelect(node).get(revision - 1);
}
public static int getRevisions(SchematicNode node) {
return getRevisions.select(rs -> {
if (rs.next()) {
return rs.getInt("CNT");
} else {
return new NodeData(node.getId(), SchematicFormat.MCEDIT);
return 0;
}
}, node);
}
public static void saveFromStream(SchematicNode node, InputStream blob, SchematicFormat format) {
updateDatabase.update(node.getId(), format, blob);
}
@Field(keys = {Table.PRIMARY})
private final int nodeId;
@Field
private Timestamp createdAt;
@Field
private SchematicFormat nodeFormat;
@@ -84,15 +110,19 @@ public class NodeData {
} catch (IOException e) {
throw new SecurityException("SchemData is wrong", e);
}
}, nodeId);
}, nodeId, createdAt);
} catch (Exception e) {
throw new IOException(e);
}
}
@Deprecated
public void saveFromStream(InputStream blob, SchematicFormat newFormat) {
updateDatabase.update(nodeId, newFormat, blob);
nodeFormat = newFormat;
saveFromStream(SchematicNode.getSchematicNode(nodeId), blob, newFormat);
}
public void delete() {
delete.update(nodeId, createdAt);
}
@AllArgsConstructor
@@ -42,13 +42,13 @@ public class SchematicNode {
TAB_CACHE.clear();
}
private static final String nodeSelector = "SELECT NodeId, NodeOwner, NodeOwner AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode ";
private static final String nodeSelector = "SELECT NodeId, NodeOwner, NodeOwner AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode ";
private static final Table<SchematicNode> table = new Table<>(SchematicNode.class);
private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem",
"NodeType");
private static final Statement update = table.update(Table.PRIMARY, "NodeName", "ParentNode", "NodeItem",
"NodeType", "NodeRank", "ReplaceColor", "AllowReplay");
"NodeType", "NodeRank", "Config");
private static final Statement delete = table.delete(Table.PRIMARY);
private static final SelectStatement<SchematicNode> byId = new SelectStatement<>(table,
@@ -66,13 +66,13 @@ public class SchematicNode {
private static final SelectStatement<SchematicNode> all = new SelectStatement<>(table,
"WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId");
private static final SelectStatement<SchematicNode> list = new SelectStatement<>(table,
"SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId "
"SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId "
+ Statement.NULL_SAFE_EQUALS
+ "? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName");
+ "? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byParentName = new SelectStatement<>(table,
"SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId "
"SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId "
+ Statement.NULL_SAFE_EQUALS
+ "? AND NM.UserId = ? AND SchematicNode.NodeName = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?");
+ "? AND NM.UserId = ? AND SchematicNode.NodeName = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?");
private static final SelectStatement<SchematicNode> schematicAccessibleForUser = new SelectStatement<>(table,
"WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeId = ?");
private static final SelectStatement<SchematicNode> accessibleByUserTypeInParent = new SelectStatement<>(table,
@@ -81,7 +81,7 @@ public class SchematicNode {
private static final SelectStatement<SchematicNode> accessibleByUserType = new SelectStatement<>(table,
"WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeType = ?");
private static final SelectStatement<SchematicNode> byIdAndUser = new SelectStatement<>(table,
"SELECT NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE NodeId = ?");
"SELECT NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE NodeId = ?");
private static final SelectStatement<SchematicNode> allParentsOfNode = new SelectStatement<>(table,
"WITH RECURSIVE R AS (SELECT NodeId, ParentNode FROM EffectiveSchematicNode WHERE NodeId = ? AND EffectiveOwner = ? UNION SELECT E.NodeId, E.ParentNode FROM R, EffectiveSchematicNode E WHERE R.ParentNode = E.NodeId AND E.EffectiveOwner = ?) SELECT SN.NodeId, SN.NodeOwner, ? AS EffectiveOwner, SN.NodeName, R.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.ReplaceColor, SN.AllowReplay FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId");
@@ -108,10 +108,8 @@ public class SchematicNode {
private SchematicType nodeType;
@Field(def = "0")
private int nodeRank;
@Field(def = "1")
private boolean replaceColor;
@Field(def = "1")
private boolean allowReplay;
@Field
private int config;
private String brCache;
@@ -125,8 +123,7 @@ public class SchematicNode {
String nodeItem,
SchematicType nodeType,
int nodeRank,
boolean replaceColor,
boolean allowReplay) {
int config) {
this.nodeId = nodeId;
this.nodeOwner = nodeOwner;
this.effectiveOwner = effectiveOwner;
@@ -136,8 +133,7 @@ public class SchematicNode {
this.nodeType = nodeType;
this.lastUpdate = lastUpdate;
this.nodeRank = nodeRank;
this.replaceColor = replaceColor;
this.allowReplay = allowReplay;
this.config = config;
}
public static List<SchematicNode> getAll(SteamwarUser user) {
@@ -407,7 +403,7 @@ public class SchematicNode {
public String getFileEnding() {
if (isDir())
throw new SecurityException("Node is Directory");
return NodeData.get(this).getNodeFormat().getFileEnding();
return NodeData.getLatest(this).getNodeFormat().getFileEnding();
}
public int getRank() {
@@ -441,24 +437,45 @@ public class SchematicNode {
}
public boolean replaceColor() {
return replaceColor;
return getConfig(ConfigFlags.REPLACE_COLOR);
}
public void setReplaceColor(boolean replaceColor) {
if (isDir())
throw new SecurityException("Is Directory");
this.replaceColor = replaceColor;
updateDB();
setConfig(ConfigFlags.REPLACE_COLOR, replaceColor);
}
public boolean allowReplay() {
return allowReplay;
return getConfig(ConfigFlags.ALLOW_REPLAY);
}
public void setAllowReplay(boolean allowReplay) {
if (isDir())
throw new SecurityException("Is Directory");
this.allowReplay = allowReplay;
setConfig(ConfigFlags.ALLOW_REPLAY, allowReplay);
}
public boolean isPrepared() {
return getConfig(ConfigFlags.IS_PREPARED);
}
public void setPrepared(boolean prepared) {
if (isDir())
throw new SecurityException("Is Directory");
setConfig(ConfigFlags.IS_PREPARED, prepared);
}
public boolean getConfig(ConfigFlags flag) {
return (config & (1 << flag.ordinal())) == 1;
}
public void setConfig(ConfigFlags flag, boolean value) {
if (value) {
config |= (1 << flag.ordinal());
} else {
config &= ~(1 << flag.ordinal());
}
updateDB();
}
@@ -486,7 +503,7 @@ public class SchematicNode {
private void updateDB() {
this.lastUpdate = Timestamp.from(Instant.now());
update.update(nodeName, parentNode, nodeItem, nodeType, nodeRank, replaceColor, allowReplay, nodeId);
update.update(nodeName, parentNode, nodeItem, nodeType, nodeRank, config, nodeId);
TAB_CACHE.clear();
}
@@ -608,4 +625,10 @@ public class SchematicNode {
TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list);
return list;
}
public static enum ConfigFlags {
REPLACE_COLOR,
ALLOW_REPLAY,
IS_PREPARED
}
}