Compare commits

...
Author SHA1 Message Date
Chaoscaot 07bf47481a Fix
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-12 20:52:36 +02:00
Chaoscaot 5305aaf669 Normalize block entity IDs and handle bundled packets
- Normalize hidden block entity identifiers across versions
- Add block break and tile entity handling for newer protocol versions
- Preserve packet filtering through bundle packets and fix chunk section copying
2026-05-11 23:40:36 +02:00
YoyoNow 1ea8dea381 Fix WhoisCommand.whois 2026-05-11 21:03:51 +02:00
Chaoscaot 15aa0572f3 Merge pull request 'Implement old bau behavior for WGS' (#332) from old-bau-behavior into main
Reviewed-on: SteamWar/SteamWar#332
Reviewed-by: D4rkr34lm <dark@steamwar.de>
2026-05-10 18:55:25 +02:00
Chaoscaot 6a843f4a71 Implement old bau behavior for WGS
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-10 11:29:26 +02:00
Chaoscaot a63c1a94ca Fix TNT explosion logic to handle non-TNT entities correctly
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-09 21:10:43 +02:00
Chaoscaot 43263035d9 Merge pull request 'Add DB Indexes for future local Development deployments' (#262) from add-db-indexes into main
Reviewed-on: SteamWar/SteamWar#262
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2026-05-09 20:23:56 +02:00
Chaoscaot 42ab55d0f8 Merge branch 'main' into add-db-indexes
# Conflicts:
#	CommonCore/SQL/src/de/steamwar/sql/BannedUserIPs.kt
2026-05-09 20:22:15 +02:00
YoyoNow 8ade5180cb Fix FightSystem 2026-05-08 20:55:17 +02:00
Chaoscaot 73f903fc23 Merge branch 'main' into add-db-indexes 2026-01-23 23:04:11 +01:00
Chaoscaot 22ed7e23da Add DB Indexes for future local Development deployments
Signed-off-by: Chaoscaot <max@maxsp.de>
2025-12-25 20:51:57 +01:00
41 changed files with 387 additions and 91 deletions
@@ -67,11 +67,7 @@ public class TNTListener implements Listener, ScoreboardElement {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onExplode(EntityExplodeEvent event) { public void onExplode(EntityExplodeEvent event) {
if (!(event.getEntity() instanceof TNTPrimed)) { explode(event.blockList(), event.getEntity() instanceof TNTPrimed);
event.blockList().clear();
return;
}
explode(event.blockList(), true);
} }
@Override @Override
@@ -28,11 +28,11 @@ import org.jetbrains.exposed.v1.javatime.timestamp
import java.time.Instant import java.time.Instant
object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") { object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") {
val time = timestamp("Time") val time = timestamp("Time").index()
val server = varchar("ServerName", 255) val server = varchar("ServerName", 255).index()
val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable() val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable().index()
val actor = reference("Actor", SteamwarUserTable) val actor = reference("Actor", SteamwarUserTable).index()
val action = enumerationByName("ActionType", 255, AuditLog.Type::class) val action = enumerationByName("ActionType", 255, AuditLog.Type::class).index()
val actionText = text("ActionText") val actionText = text("ActionText")
} }
@@ -33,8 +33,8 @@ import java.sql.Timestamp
import java.time.Instant import java.time.Instant
object BannedUserIPsTable: CompositeIdTable("BannedUserIPs") { object BannedUserIPsTable: CompositeIdTable("BannedUserIPs") {
val userId = reference("UserID", SteamwarUserTable) val userId = reference("UserID", SteamwarUserTable).index()
val timestamp = timestamp("Timestamp") val timestamp = timestamp("Timestamp").index()
val ip = varchar("IP", 45).entityId() val ip = varchar("IP", 45).entityId()
init { init {
@@ -32,8 +32,8 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.util.* import java.util.*
object BauweltMemberTable: CompositeIdTable("BauweltMember") { object BauweltMemberTable: CompositeIdTable("BauweltMember") {
val bauweltId = reference("BauweltID", SteamwarUserTable) val bauweltId = reference("BauweltID", SteamwarUserTable).index()
val memberId = reference("MemberID", SteamwarUserTable) val memberId = reference("MemberID", SteamwarUserTable).index()
val build = bool("Build") val build = bool("Build")
val worldEdit = bool("WorldEdit") val worldEdit = bool("WorldEdit")
val world = bool("World") val world = bool("World")
@@ -20,6 +20,7 @@
package de.steamwar.sql package de.steamwar.sql
import de.steamwar.sql.internal.useDb import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.SortOrder import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.core.and import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.CompositeID import org.jetbrains.exposed.v1.core.dao.id.CompositeID
@@ -34,19 +35,23 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.sql.Timestamp import java.sql.Timestamp
object CheckedSchematicTable: CompositeIdTable("CheckedSchematic") { object CheckedSchematicTable: CompositeIdTable("CheckedSchematic") {
val nodeId = optReference("NodeId", SchematicNodeTable) val nodeId = optReference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL, onUpdate = ReferenceOption.SET_NULL).index()
val nodeOwner = reference("NodeOwner", SteamwarUserTable) val nodeOwner = reference("NodeOwner", SteamwarUserTable).index()
val nodeName = varchar("NodeName", 64).entityId() val nodeName = varchar("NodeName", 64).entityId().index()
val validator = reference("Validator", SteamwarUserTable) val validator = reference("Validator", SteamwarUserTable).index()
val startTime = timestamp("StartTime").entityId() val startTime = timestamp("StartTime").entityId().index()
val endTime = timestamp("EndTime") val endTime = timestamp("EndTime")
val declineReason = text("DeclineReason") val declineReason = text("DeclineReason")
val seen = bool("Seen") val seen = bool("Seen").index()
val nodeType = varchar("NodeType", 16) val nodeType = varchar("NodeType", 16)
init { init {
addIdColumn(nodeOwner) addIdColumn(nodeOwner)
addIdColumn(nodeName) addIdColumn(nodeName)
index(false, nodeOwner, endTime)
index(false, startTime, endTime, nodeName)
index(false, seen, nodeOwner, startTime)
} }
} }
+2 -2
View File
@@ -36,8 +36,8 @@ import java.time.Instant
object EventTable : IntIdTable("Event", "EventId") { object EventTable : IntIdTable("Event", "EventId") {
val name = varchar("EventName", 100).uniqueIndex() val name = varchar("EventName", 100).uniqueIndex()
val deadline = timestamp("Deadline") val deadline = timestamp("Deadline")
val start = timestamp("Start") val start = timestamp("Start").index()
val end = timestamp("End") val end = timestamp("End").index()
val maxPlayers = integer("MaximumTeamMembers") val maxPlayers = integer("MaximumTeamMembers")
val schemType = varchar("SchemType", 16).nullable() val schemType = varchar("SchemType", 16).nullable()
val publicsOnly = bool("PublicSchemsOnly") val publicsOnly = bool("PublicSchemsOnly")
@@ -33,17 +33,17 @@ import java.time.Instant
import java.util.* import java.util.*
object EventFightTable : IntIdTable("EventFight", "FightID") { object EventFightTable : IntIdTable("EventFight", "FightID") {
val eventId = reference("EventID", EventTable) val eventId = reference("EventID", EventTable).index()
val startTime = timestamp("StartTime") val startTime = timestamp("StartTime").index()
val gamemode = text("Spielmodus") val gamemode = text("Spielmodus")
val map = text("Map") val map = text("Map")
val groupId = optReference("GroupId", EventGroupTable) val groupId = optReference("GroupId", EventGroupTable).index()
val teamBlue = reference("TeamBlue", TeamTable) val teamBlue = reference("TeamBlue", TeamTable).index()
val teamRed = reference("TeamRed", TeamTable) val teamRed = reference("TeamRed", TeamTable).index()
val spectatePort = integer("SpectatePort").nullable() val spectatePort = integer("SpectatePort").nullable()
val bestOf = integer("BestOf") val bestOf = integer("BestOf")
val ergebnis = integer("Ergebnis") val ergebnis = integer("Ergebnis")
val fight = optReference("Fight", FightTable) val fight = optReference("Fight", FightTable).index()
} }
class EventFight(id: EntityID<Int>) : IntEntity(id), Comparable<EventFight> { class EventFight(id: EntityID<Int>) : IntEntity(id), Comparable<EventFight> {
@@ -34,6 +34,10 @@ object EventGroupTable : IntIdTable("EventGroup", "Id") {
val pointsPerWin = integer("PointsPerWin").default(3) val pointsPerWin = integer("PointsPerWin").default(3)
val pointsPerLoss = integer("PointsPerLoss").default(0) val pointsPerLoss = integer("PointsPerLoss").default(0)
val pointsPerDraw = integer("PointsPerDraw").default(1) val pointsPerDraw = integer("PointsPerDraw").default(1)
init {
uniqueIndex(event, name)
}
} }
class EventGroup(id: EntityID<Int>) : IntEntity(id) { class EventGroup(id: EntityID<Int>) : IntEntity(id) {
@@ -29,7 +29,7 @@ import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.select import org.jetbrains.exposed.v1.jdbc.select
object EventRelationTable : IntIdTable("EventRelation") { object EventRelationTable : IntIdTable("EventRelation") {
val fightId = reference("FightId", EventFightTable) val fightId = reference("FightId", EventFightTable).index()
val fightTeam = enumeration("FightTeam", EventRelation.FightTeam::class) val fightTeam = enumeration("FightTeam", EventRelation.FightTeam::class)
val fromType = enumeration("FromType", EventRelation.FromType::class) val fromType = enumeration("FromType", EventRelation.FromType::class)
val fromId = integer("FromId") val fromId = integer("FromId")
+5 -5
View File
@@ -34,14 +34,14 @@ import org.jetbrains.exposed.v1.jdbc.update
import java.sql.Timestamp import java.sql.Timestamp
object FightTable : IntIdTable("Fight", "FightId") { object FightTable : IntIdTable("Fight", "FightId") {
val gamemode = varchar("Gamemode", 30) val gamemode = varchar("Gamemode", 30).index()
val server = text("Server") val server = text("Server")
val startTime = timestamp("StartTime") val startTime = timestamp("StartTime")
val duration = integer("Duration") val duration = integer("Duration")
val blueLeader = reference("BlueLeader", SteamwarUserTable) val blueLeader = reference("BlueLeader", SteamwarUserTable).index()
val redLeader = reference("RedLeader", SteamwarUserTable) val redLeader = reference("RedLeader", SteamwarUserTable).index()
val blueSchem = optReference("BlueSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL) val blueSchem = optReference("BlueSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL).index()
val redSchem = optReference("RedSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL) val redSchem = optReference("RedSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL).index()
val win = enumeration("Win", Fight.WinningTeam::class) val win = enumeration("Win", Fight.WinningTeam::class)
val winCondition = varchar("WinCondition", 100) val winCondition = varchar("WinCondition", 100)
val replayAvailable = bool("ReplayAvailable") val replayAvailable = bool("ReplayAvailable")
@@ -30,7 +30,7 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
object FightPlayerTable : CompositeIdTable("FightPlayer") { object FightPlayerTable : CompositeIdTable("FightPlayer") {
val fightId = reference("FightId", FightTable) val fightId = reference("FightId", FightTable)
val userId = reference("UserId", SteamwarUserTable) val userId = reference("UserId", SteamwarUserTable).index()
val team = integer("Team") val team = integer("Team")
val kit = varchar("Kit", 64) val kit = varchar("Kit", 64)
val kills = integer("Kills") val kills = integer("Kills")
@@ -30,8 +30,8 @@ import org.jetbrains.exposed.v1.dao.CompositeEntityClass
import java.util.* import java.util.*
object IgnoreSystemTable: CompositeIdTable("IgnoredPlayers") { object IgnoreSystemTable: CompositeIdTable("IgnoredPlayers") {
val ignorer = reference("Ignorer", SteamwarUserTable) val ignorer = reference("Ignorer", SteamwarUserTable).index()
val ignored = reference("Ignored", SteamwarUserTable) val ignored = reference("Ignored", SteamwarUserTable).index()
override val primaryKey = PrimaryKey(ignorer, ignored) override val primaryKey = PrimaryKey(ignorer, ignored)
@@ -35,7 +35,7 @@ import java.io.InputStream
import java.util.zip.GZIPInputStream import java.util.zip.GZIPInputStream
object NodeDataTable: CompositeIdTable("NodeData") { object NodeDataTable: CompositeIdTable("NodeData") {
val nodeId = reference("NodeId", SchematicNodeTable) val nodeId = reference("NodeId", SchematicNodeTable).index()
val createdAt = timestamp("CreatedAt").defaultExpression(CurrentTimestamp).entityId() val createdAt = timestamp("CreatedAt").defaultExpression(CurrentTimestamp).entityId()
val nodeFormat = enumeration("NodeFormat", NodeData.SchematicFormat::class) val nodeFormat = enumeration("NodeFormat", NodeData.SchematicFormat::class)
val schemData = blob("SchemData") val schemData = blob("SchemData")
@@ -20,6 +20,7 @@
package de.steamwar.sql package de.steamwar.sql
import de.steamwar.sql.internal.useDb import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IdTable import org.jetbrains.exposed.v1.core.dao.id.IdTable
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.v1.core.eq
@@ -32,8 +33,8 @@ import java.sql.Timestamp
import java.time.Instant import java.time.Instant
object NodeDownloadTable: IdTable<Int>("NodeDownload") { object NodeDownloadTable: IdTable<Int>("NodeDownload") {
override val id = reference("NodeId", SchematicNodeTable).uniqueIndex() override val id = reference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.CASCADE).uniqueIndex()
val link = varchar("Link", 255) val link = varchar("Link", 255).uniqueIndex()
val timestamp = timestamp("Timestamp").defaultExpression(CurrentTimestamp) val timestamp = timestamp("Timestamp").defaultExpression(CurrentTimestamp)
} }
@@ -20,6 +20,7 @@
package de.steamwar.sql package de.steamwar.sql
import de.steamwar.sql.internal.useDb import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.and import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.CompositeID import org.jetbrains.exposed.v1.core.dao.id.CompositeID
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
@@ -32,9 +33,9 @@ import java.util.*
import kotlin.jvm.optionals.getOrNull import kotlin.jvm.optionals.getOrNull
object NodeMemberTable : CompositeIdTable("NodeMember") { object NodeMemberTable : CompositeIdTable("NodeMember") {
val node = reference("NodeId", SchematicNodeTable) val node = reference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE).index()
val userId = reference("UserId", SteamwarUserTable) val userId = reference("UserId", SteamwarUserTable).index()
val parentNode = optReference("ParentId", SchematicNodeTable) val parentNode = optReference("ParentId", SchematicNodeTable).index()
override val primaryKey = PrimaryKey(node, userId) override val primaryKey = PrimaryKey(node, userId)
@@ -41,6 +41,7 @@ object PersonalKitTable: CompositeIdTable("PersonalKit") {
init { init {
addIdColumn(userId) addIdColumn(userId)
index(false, userId, gamemode)
} }
} }
@@ -34,12 +34,16 @@ import java.util.function.Consumer
object PunishmentTable : IntIdTable("Punishments", "PunishmentId") { object PunishmentTable : IntIdTable("Punishments", "PunishmentId") {
val userId = reference("UserId", SteamwarUserTable) val userId = reference("UserId", SteamwarUserTable)
val punisher = reference("Punisher", SteamwarUserTable) val punisher = reference("Punisher", SteamwarUserTable).index()
val type = enumerationByName("Type", 32, Punishment.PunishmentType::class) val type = enumerationByName("Type", 32, Punishment.PunishmentType::class)
val startTime = timestamp("StartTime") val startTime = timestamp("StartTime")
val endTime = timestamp("EndTime") val endTime = timestamp("EndTime")
val perma = bool("Perma") val perma = bool("Perma")
val reason = text("Reason") val reason = text("Reason")
init {
index(false, userId, type)
}
} }
class Punishment(id: EntityID<Int>) : IntEntity(id) { class Punishment(id: EntityID<Int>) : IntEntity(id) {
@@ -30,7 +30,7 @@ import org.jetbrains.exposed.v1.dao.CompositeEntityClass
object RefereeTable: CompositeIdTable("Referee") { object RefereeTable: CompositeIdTable("Referee") {
val eventId = reference("EventId", EventTable) val eventId = reference("EventId", EventTable)
val userId = reference("UserId", SteamwarUserTable) val userId = reference("UserId", SteamwarUserTable).index()
override val primaryKey = PrimaryKey(eventId, userId) override val primaryKey = PrimaryKey(eventId, userId)
@@ -34,13 +34,17 @@ import java.util.*
import java.util.function.Consumer import java.util.function.Consumer
object SchematicNodeTable : IntIdTable("SchematicNode", "NodeId") { object SchematicNodeTable : IntIdTable("SchematicNode", "NodeId") {
val owner = reference("NodeOwner", SteamwarUserTable) val owner = reference("NodeOwner", SteamwarUserTable).index()
val name = varchar("NodeName", 64) val name = varchar("NodeName", 64)
val parent = optReference("ParentNode", SchematicNodeTable) val parent = optReference("ParentNode", SchematicNodeTable).index()
val lastUpdate = timestamp("LastUpdate").defaultExpression(CurrentTimestamp) val lastUpdate = timestamp("LastUpdate").defaultExpression(CurrentTimestamp)
val item = text("NodeItem") val item = text("NodeItem")
val type = varchar("NodeType", 16).nullable() val type = varchar("NodeType", 16).nullable().index()
val config = integer("Config") val config = integer("Config")
init {
uniqueIndex(parent, owner, name)
}
} }
class SchematicNode(id: EntityID<Int>) : IntEntity(id) { class SchematicNode(id: EntityID<Int>) : IntEntity(id) {
+5 -1
View File
@@ -28,9 +28,13 @@ import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass import org.jetbrains.exposed.v1.dao.IntEntityClass
object ScriptTable: IntIdTable("Script") { object ScriptTable: IntIdTable("Script") {
val userId = reference("UserId", SteamwarUserTable) val userId = reference("UserId", SteamwarUserTable).index()
val name = varchar("Name", 64) val name = varchar("Name", 64)
val code = text("Code") val code = text("Code")
init {
uniqueIndex(userId, name)
}
} }
class Script(id: EntityID<Int>) : IntEntity(id) { class Script(id: EntityID<Int>) : IntEntity(id) {
@@ -28,7 +28,7 @@ import org.jetbrains.exposed.v1.jdbc.insert
import java.sql.Timestamp import java.sql.Timestamp
object SessionTable: Table("Session") { object SessionTable: Table("Session") {
val userId = reference("UserId", SteamwarUserTable) val userId = reference("UserId", SteamwarUserTable).index()
val startTime = timestamp("StartTime") val startTime = timestamp("StartTime")
val endTime = timestamp("EndTime").defaultExpression(CurrentTimestamp) val endTime = timestamp("EndTime").defaultExpression(CurrentTimestamp)
} }
@@ -37,15 +37,15 @@ import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec import javax.crypto.spec.PBEKeySpec
object SteamwarUserTable : IntIdTable("UserData", "id") { object SteamwarUserTable : IntIdTable("UserData", "id") {
val uuid = varchar("UUID", 36) val uuid = varchar("UUID", 36).uniqueIndex()
val username = varchar("UserName", 32) val username = varchar("UserName", 32).index()
val team = reference("Team", TeamTable) val team = reference("Team", TeamTable).index()
val leader = bool("Leader") val leader = bool("Leader")
val locale = varchar("Locale", 16).nullable() val locale = varchar("Locale", 16).nullable()
val manualLocale = bool("ManualLocale") val manualLocale = bool("ManualLocale")
val bedrock = bool("Bedrock") val bedrock = bool("Bedrock")
val password = text("Password").nullable() val password = text("Password").nullable()
val discordId = long("DiscordId").nullable() val discordId = long("DiscordId").nullable().uniqueIndex()
} }
class SteamwarUser(id: EntityID<Int>): IntEntity(id) { class SteamwarUser(id: EntityID<Int>): IntEntity(id) {
+2 -2
View File
@@ -28,9 +28,9 @@ import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.select import org.jetbrains.exposed.v1.jdbc.select
object TeamTable : IntIdTable("Team", "TeamID") { object TeamTable : IntIdTable("Team", "TeamID") {
val kuerzel = varchar("TeamKuerzel", 10) val kuerzel = varchar("TeamKuerzel", 10).index()
val color = char("TeamColor", 1).default("8") val color = char("TeamColor", 1).default("8")
val name = varchar("TeamName", 16) val name = varchar("TeamName", 16).index()
val deleted = bool("TeamDeleted").default(false) val deleted = bool("TeamDeleted").default(false)
} }
@@ -32,8 +32,8 @@ import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.insertIgnore import org.jetbrains.exposed.v1.jdbc.insertIgnore
object TeamTeilnahmeTable : CompositeIdTable("TeamTeilnahme") { object TeamTeilnahmeTable : CompositeIdTable("TeamTeilnahme") {
val teamId = reference("teamId", TeamTable) val teamId = reference("teamId", TeamTable).index()
val eventId = reference("eventId", EventTable) val eventId = reference("eventId", EventTable).index()
val placement = integer("Placement").nullable() val placement = integer("Placement").nullable()
override val primaryKey = PrimaryKey(teamId, eventId) override val primaryKey = PrimaryKey(teamId, eventId)
+3 -3
View File
@@ -33,10 +33,10 @@ import java.sql.Timestamp
import java.util.* import java.util.*
object TokenTable: IntIdTable("Token") { object TokenTable: IntIdTable("Token") {
val name = varchar("Name", 64) val name = varchar("Name", 64).uniqueIndex()
val owner = reference("Owner", SteamwarUserTable) val owner = reference("Owner", SteamwarUserTable).index()
val created = timestamp("Created").defaultExpression(CurrentTimestamp) val created = timestamp("Created").defaultExpression(CurrentTimestamp)
val hash = varchar("Hash", 88) val hash = varchar("Hash", 88).uniqueIndex()
} }
class Token(id: EntityID<Int>): IntEntity(id) { class Token(id: EntityID<Int>): IntEntity(id) {
@@ -28,7 +28,7 @@ import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.selectAll import org.jetbrains.exposed.v1.jdbc.selectAll
object UserPermTable: Table("UserPerm") { object UserPermTable: Table("UserPerm") {
val user = reference("User", SteamwarUserTable.id) val user = reference("User", SteamwarUserTable.id).index()
val perm = enumerationByName("Perm", 32, UserPerm::class) val perm = enumerationByName("Perm", 32, UserPerm::class)
override val primaryKey = PrimaryKey(user, perm) override val primaryKey = PrimaryKey(user, perm)
@@ -67,7 +67,7 @@ object KotlinDatabase {
} }
} }
fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T { fun <T> useDb(statement: JdbcTransaction.() -> T): T {
KotlinDatabase.ensureConnected() KotlinDatabase.ensureConnected()
return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db) { return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db) {
statement() statement()
@@ -98,10 +98,12 @@ public class FightSystem extends JavaPlugin {
new StateDependentListener(ArenaMode.All, FightState.All, BountifulWrapper.impl.newDenyArrowPickupListener()); new StateDependentListener(ArenaMode.All, FightState.All, BountifulWrapper.impl.newDenyArrowPickupListener());
new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f)); new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f));
new OneShotStateDependent(ArenaMode.Test, FightState.All, WorldEditRendererCUIEditor::new); new OneShotStateDependent(ArenaMode.Test, FightState.All, WorldEditRendererCUIEditor::new);
try { if (Core.getVersion() >= 19) {
Bukkit.getWorlds().get(0).setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode)); try {
} catch (Exception e) { Bukkit.getWorlds().get(0).setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode));
// Ignore if failed! } catch (Exception e) {
// Ignore if failed!
}
} }
techHider = new TechHiderWrapper(); techHider = new TechHiderWrapper();
@@ -181,7 +181,12 @@ public class Permanent implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onExplosion(EntityExplodeEvent e) { public void onExplosion(EntityExplodeEvent e) {
if (!(e.getEntity() instanceof TNTPrimed)) return; if (!(e.getEntity() instanceof TNTPrimed)) {
if (Config.GameModeConfig.Schematic.Type.toDB().equals("wargearseason26")) {
e.blockList().clear();
}
return;
}
if (!Config.GameModeConfig.Arena.WaterDamage) return; if (!Config.GameModeConfig.Arena.WaterDamage) return;
e.blockList().removeIf(block -> { e.blockList().removeIf(block -> {
if(block.getType() == Material.TNT) { if(block.getType() == Material.TNT) {
@@ -93,7 +93,7 @@ public class ChunkHider18 implements ChunkHider {
private static final Reflection.Method getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class); private static final Reflection.Method getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class);
private static final Reflection.Method getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class); private static final Reflection.Method getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class);
protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) { protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) {
return !hiddenBlockEntities.contains((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile)))); return !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile)))));
} }
private void blocks(SectionHider section) { private void blocks(SectionHider section) {
@@ -20,8 +20,11 @@
package de.steamwar.techhider; package de.steamwar.techhider;
import de.steamwar.Reflection; import de.steamwar.Reflection;
import de.steamwar.core.Core;
import net.minecraft.core.IRegistry;
import net.minecraft.core.SectionPosition; import net.minecraft.core.SectionPosition;
import net.minecraft.network.protocol.game.PacketPlayOutBlockBreak; import net.minecraft.network.protocol.game.PacketPlayOutBlockBreak;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.world.level.block.entity.TileEntitySign; import net.minecraft.world.level.block.entity.TileEntitySign;
import net.minecraft.world.level.block.entity.TileEntityTypes; import net.minecraft.world.level.block.entity.TileEntityTypes;
import net.minecraft.world.level.block.state.IBlockData; import net.minecraft.world.level.block.state.IBlockData;
@@ -88,6 +91,14 @@ public class ProtocolWrapper18 implements ProtocolWrapper {
return tileEntityType.get(packet) != signType; return tileEntityType.get(packet) != signType;
} }
private static final IRegistry<?> tileEntityTypes = Reflection.getField(Core.getVersion() > 18 ? Reflection.getClass("net.minecraft.core.registries.BuiltInRegistries") : IRegistry.class, IRegistry.class, 0, TileEntityTypes.class).get(null);
private static final Reflection.Method getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class);
private static final Reflection.Method getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class);
@Override
public String tileEntityDataType(Object packet) {
return TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(tileEntityTypes, tileEntityType.get(packet))));
}
@Override @Override
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) { public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) {
return (p, packet) -> { return (p, packet) -> {
@@ -19,6 +19,7 @@
package de.steamwar.techhider; package de.steamwar.techhider;
import de.steamwar.Reflection;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@@ -27,6 +28,7 @@ public class ProtocolWrapper19 extends ProtocolWrapper18 {
@Override @Override
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) { public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) {
return null; Reflection.Field<?> blockBreakPosition = Reflection.getField(blockBreakPacket, TechHider.blockPosition, 0);
return (p, packet) -> techHider.getLocationEvaluator().checkBlockPos(p, blockBreakPosition.get(packet)) == TechHider.State.SKIP ? packet : null;
} }
} }
@@ -25,16 +25,20 @@ import io.netty.buffer.Unpooled;
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.util.SimpleBitStorage; import net.minecraft.util.SimpleBitStorage;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.IntUnaryOperator;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class ChunkHider21 implements ChunkHider { public class ChunkHider21 implements ChunkHider {
private static final int DIRECT_BLOCK_BITS = Math.max(9, Integer.SIZE - Integer.numberOfLeadingZeros(Block.BLOCK_STATE_REGISTRY.size() - 1));
@Override @Override
public Class<?> mapChunkPacket() { public Class<?> mapChunkPacket() {
return ClientboundLevelChunkWithLightPacket.class; return ClientboundLevelChunkWithLightPacket.class;
@@ -96,33 +100,70 @@ public class ChunkHider21 implements ChunkHider {
private static final Reflection.Method getKey = Reflection.getTypedMethod(registry, "getKey", resourceLocation, Object.class); private static final Reflection.Method getKey = Reflection.getTypedMethod(registry, "getKey", resourceLocation, Object.class);
private static final Reflection.Method getName = Reflection.getTypedMethod(resourceLocation, "getPath", String.class); private static final Reflection.Method getName = Reflection.getTypedMethod(resourceLocation, "getPath", String.class);
protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) { protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) {
return !hiddenBlockEntities.contains(getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile)))); return !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile)))));
} }
private void blocks(SectionHider section) { private void blocks(SectionHider section) {
section.copyBitsPerBlock(); int sourceBitsPerBlock = section.readBitsPerBlock();
boolean singleValued = section.getBitsPerBlock() == 0; if(section.isSkipSection()) {
section.writeBitsPerBlock(sourceBitsPerBlock);
copyBlockStorage(section);
return;
}
boolean singleValued = sourceBitsPerBlock == 0;
if (singleValued) { if (singleValued) {
int value = ProtocolUtils.readVarInt(section.getIn()); int value = ProtocolUtils.readVarInt(section.getIn());
ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getObfuscate().contains(value) ? section.getTarget() : value); if(section.blockPrecise()) {
writeDirectBlocks(section, DIRECT_BLOCK_BITS, ignored -> value);
} else {
section.writeBitsPerBlock(sourceBitsPerBlock);
ProtocolUtils.writeVarInt(section.getOut(), section.getObfuscate().contains(value) ? section.getTarget() : value);
}
return; return;
} else if (section.getBitsPerBlock() < 9) { } else if (sourceBitsPerBlock < 9) {
if(section.blockPrecise()) {
int[] palette = section.readPalette();
SimpleBitStorage values = new SimpleBitStorage(sourceBitsPerBlock, 4096, section.readNewDataArray(4096));
writeDirectBlocks(section, DIRECT_BLOCK_BITS, pos -> palette[values.get(pos)]);
return;
}
section.writeBitsPerBlock(sourceBitsPerBlock);
// Indirect (paletted) storage – only present when bitsPerBlock < 9 in 1.21+ // Indirect (paletted) storage – only present when bitsPerBlock < 9 in 1.21+
section.processPalette(); section.processPalette();
} }
if (section.isSkipSection() || (!section.blockPrecise() && section.isPaletted())) { if (!section.blockPrecise() && section.isPaletted()) {
section.skipNewDataArray(4096); section.skipNewDataArray(4096);
return; return;
} }
SimpleBitStorage values = new SimpleBitStorage(section.getBitsPerBlock(), 4096, section.readNewDataArray(4096)); SimpleBitStorage values = new SimpleBitStorage(sourceBitsPerBlock, 4096, section.readNewDataArray(4096));
writeDirectBlocks(section, sourceBitsPerBlock, values::get);
}
private void copyBlockStorage(SectionHider section) {
if(section.getBitsPerBlock() == 0) {
section.copyVarInt();
} else {
if(section.getBitsPerBlock() < 9)
section.skipPalette();
section.skipNewDataArray(4096);
}
}
private void writeDirectBlocks(SectionHider section, int bitsPerBlock, IntUnaryOperator source) {
section.writeBitsPerBlock(bitsPerBlock);
SimpleBitStorage values = new SimpleBitStorage(bitsPerBlock, 4096);
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) { for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) { for (int x = 0; x < 16; x++) {
int pos = (((y * 16) + z) * 16) + x; int pos = (((y * 16) + z) * 16) + x;
int value = source.applyAsInt(pos);
TechHider.State test = section.test(x, y, z); TechHider.State test = section.test(x, y, z);
@@ -130,15 +171,17 @@ public class ChunkHider21 implements ChunkHider {
case SKIP: case SKIP:
break; break;
case CHECK: case CHECK:
if (!section.getObfuscate().contains(values.get(pos))) if (!section.getObfuscate().contains(value))
break; break;
case HIDE: case HIDE:
values.set(pos, section.getTarget()); value = section.getTarget();
break; break;
case HIDE_AIR: case HIDE_AIR:
default: default:
values.set(pos, section.getAir()); value = section.getAir();
} }
values.set(pos, value);
} }
} }
} }
@@ -0,0 +1,104 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.techhider;
import de.steamwar.Reflection;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.function.BiFunction;
public class ProtocolWrapper21 implements ProtocolWrapper {
private static final Reflection.Field<SectionPos> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPos.class, 0);
private static final Reflection.Field<short[]> multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0);
private static final Reflection.Field<BlockState[]> multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, BlockState[].class, 0);
@Override
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider) {
return (p, packet) -> {
TechHider.LocationEvaluator locationEvaluator = techHider.getLocationEvaluator();
SectionPos sectionPos = multiBlockChangeChunk.get(packet);
int chunkX = sectionPos.x();
int chunkY = sectionPos.y();
int chunkZ = sectionPos.z();
if(locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ))
return packet;
packet = TechHider.multiBlockChangeCloner.apply(packet);
final short[] oldPos = multiBlockChangePos.get(packet);
final BlockState[] oldBlocks = multiBlockChangeBlocks.get(packet);
ArrayList<Short> poss = new ArrayList<>(oldPos.length);
ArrayList<BlockState> blocks = new ArrayList<>(oldPos.length);
for(int i = 0; i < oldPos.length; i++) {
short pos = oldPos[i];
BlockState block = oldBlocks[i];
switch(locationEvaluator.check(p, 16*chunkX + SectionPos.sectionRelativeX(pos), 16*chunkY + SectionPos.sectionRelativeY(pos), 16*chunkZ + SectionPos.sectionRelativeZ(pos))) {
case SKIP:
poss.add(pos);
blocks.add(block);
break;
case CHECK:
poss.add(pos);
blocks.add(techHider.iBlockDataHidden(block) ? (BlockState) techHider.getObfuscationTarget() : block);
break;
default:
break;
}
}
if(blocks.isEmpty())
return null;
short[] newPos = new short[poss.size()];
for(int i = 0; i < newPos.length; i++)
newPos[i] = poss.get(i);
multiBlockChangePos.set(packet, newPos);
multiBlockChangeBlocks.set(packet, blocks.toArray(new BlockState[0]));
return packet;
};
}
private static final Reflection.Field<BlockEntityType> tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, BlockEntityType.class, 0);
@Override
public boolean unfilteredTileEntityDataAction(Object packet) {
BlockEntityType type = tileEntityType.get(packet);
return type != BlockEntityType.SIGN && type != BlockEntityType.HANGING_SIGN;
}
@Override
public String tileEntityDataType(Object packet) {
return BlockEntityType.getKey(tileEntityType.get(packet)).getPath();
}
private static final Reflection.Field<BlockPos> blockBreakPosition = Reflection.getField(ClientboundBlockDestructionPacket.class, BlockPos.class, 0);
@Override
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) {
return (p, packet) -> techHider.getLocationEvaluator().checkBlockPos(p, blockBreakPosition.get(packet)) == TechHider.State.SKIP ? packet : null;
}
}
@@ -54,7 +54,7 @@ public class ChunkHider9 extends ChunkHider8 {
packet = mapChunkCloner.apply(packet); packet = mapChunkCloner.apply(packet);
Set<String> hiddenBlockEntities = techHider.getHiddenBlockEntities(); Set<String> hiddenBlockEntities = techHider.getHiddenBlockEntities();
mapChunkBlockEntities.set(packet, ((List<?>)mapChunkBlockEntities.get(packet)).stream().filter( mapChunkBlockEntities.set(packet, ((List<?>)mapChunkBlockEntities.get(packet)).stream().filter(
nbttag -> !hiddenBlockEntities.contains((String) nbtTagGetString.invoke(nbttag, "id")) nbttag -> !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) nbtTagGetString.invoke(nbttag, "id")))
).collect(Collectors.toList())); ).collect(Collectors.toList()));
int primaryBitMask = mapChunkBitMask.get(packet); int primaryBitMask = mapChunkBitMask.get(packet);
@@ -23,6 +23,7 @@ import de.steamwar.Reflection;
import de.steamwar.Reflection.Field; import de.steamwar.Reflection.Field;
import de.steamwar.core.Core; import de.steamwar.core.Core;
import io.netty.channel.*; import io.netty.channel.*;
import io.netty.util.ReferenceCountUtil;
import lombok.Getter; import lombok.Getter;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -37,6 +38,7 @@ import org.bukkit.plugin.Plugin;
import java.util.*; import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.logging.Level; import java.util.logging.Level;
@@ -57,17 +59,29 @@ public class TinyProtocol implements Listener {
public static final Field<List> networkManagers = Reflection.getField(serverConnection, List.class, 0, networkManager); public static final Field<List> networkManagers = Reflection.getField(serverConnection, List.class, 0, networkManager);
private static final String HANDLER_NAME = "tiny-steamwar"; private static final String HANDLER_NAME = "tiny-steamwar";
private static final Class<?> bundlePacket = getOptionalClass("net.minecraft.network.protocol.BundlePacket");
private static final Class<?> clientboundBundlePacket = getOptionalClass("net.minecraft.network.protocol.game.ClientboundBundlePacket");
private static final Reflection.Method bundleSubPackets = bundlePacket == null ? null : Reflection.getTypedMethod(bundlePacket, null, Iterable.class);
private static final Reflection.Constructor clientboundBundleConstructor = clientboundBundlePacket == null ? null : Reflection.getConstructor(clientboundBundlePacket, Iterable.class);
public static final TinyProtocol instance = new TinyProtocol(Core.getInstance()); public static final TinyProtocol instance = new TinyProtocol(Core.getInstance());
public static void init() { public static void init() {
//enforce init //enforce init
} }
private static Class<?> getOptionalClass(String className) {
try {
return Reflection.getClass(className);
} catch (IllegalArgumentException ignored) {
return null;
}
}
private final Plugin plugin; private final Plugin plugin;
private final List<?> connections; private final List<?> connections;
private boolean closed; private boolean closed;
private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new HashMap<>(); private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new ConcurrentHashMap<>();
@Getter @Getter
private final Map<Player, PacketInterceptor> playerInterceptors = new HashMap<>(); private final Map<Player, PacketInterceptor> playerInterceptors = new HashMap<>();
@@ -207,31 +221,51 @@ public class TinyProtocol implements Listener {
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Object originalMsg = msg;
try { try {
msg = filterPacket(player, msg); msg = filterPacket(player, msg);
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error during incoming packet processing", e); plugin.getLogger().log(Level.SEVERE, "Error during incoming packet processing", e);
msg = null;
} }
if (msg != null) { if (msg != null) {
super.channelRead(ctx, msg); super.channelRead(ctx, msg);
} else {
ReferenceCountUtil.release(originalMsg);
} }
} }
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Object originalMsg = msg;
try { try {
msg = filterPacket(player, msg); msg = filterPacket(player, msg);
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error during outgoing packet processing", e); plugin.getLogger().log(Level.SEVERE, "Error during outgoing packet processing", e);
msg = null;
} }
if (msg != null) { if (msg != null) {
super.write(ctx, msg, promise); super.write(ctx, msg, promise);
} else {
ReferenceCountUtil.release(originalMsg);
promise.trySuccess();
} }
} }
private Object filterPacket(Player player, Object packet) { private Object filterPacket(Player player, Object packet) {
packet = filterSinglePacket(player, packet);
if(packet == null)
return null;
if(clientboundBundlePacket != null && clientboundBundlePacket.isInstance(packet))
return filterBundlePacket(player, packet);
return packet;
}
private Object filterSinglePacket(Player player, Object packet) {
List<BiFunction<Player, Object, Object>> filters = packetFilters.getOrDefault(packet.getClass(), Collections.emptyList()); List<BiFunction<Player, Object, Object>> filters = packetFilters.getOrDefault(packet.getClass(), Collections.emptyList());
for(BiFunction<Player, Object, Object> filter : filters) { for(BiFunction<Player, Object, Object> filter : filters) {
@@ -243,5 +277,33 @@ public class TinyProtocol implements Listener {
return packet; return packet;
} }
private Object filterBundlePacket(Player player, Object packet) {
if(bundleSubPackets == null || clientboundBundleConstructor == null)
throw new IllegalStateException("Cannot filter bundled packet " + packet.getClass().getName());
ArrayList<Object> filteredPackets = new ArrayList<>();
boolean changed = false;
for(Object subPacket : (Iterable<?>) bundleSubPackets.invoke(packet)) {
Object filteredPacket = filterPacket(player, subPacket);
if(filteredPacket == null) {
changed = true;
continue;
}
if(filteredPacket != subPacket)
changed = true;
filteredPackets.add(filteredPacket);
}
if(!changed)
return packet;
if(filteredPackets.isEmpty())
return null;
return clientboundBundleConstructor.invoke(filteredPackets);
}
} }
} }
@@ -92,7 +92,17 @@ public interface ChunkHider {
} }
public void copyBitsPerBlock() { public void copyBitsPerBlock() {
bitsPerBlock = in.readByte(); bitsPerBlock = in.readUnsignedByte();
out.writeByte(bitsPerBlock);
}
public int readBitsPerBlock() {
bitsPerBlock = in.readUnsignedByte();
return bitsPerBlock;
}
public void writeBitsPerBlock(int bitsPerBlock) {
this.bitsPerBlock = bitsPerBlock;
out.writeByte(bitsPerBlock); out.writeByte(bitsPerBlock);
} }
@@ -108,6 +118,15 @@ public interface ChunkHider {
copyVarInt(); copyVarInt();
} }
public int[] readPalette() {
int paletteLength = ProtocolUtils.readVarInt(in);
int[] palette = new int[paletteLength];
for(int i = 0; i < paletteLength; i++)
palette[i] = ProtocolUtils.readVarInt(in);
return palette;
}
public void processPalette() { public void processPalette() {
if(skipSection) { if(skipSection) {
skipPalette(); skipPalette();
@@ -98,10 +98,7 @@ public class ProtocolUtils {
} }
public static int posToChunk(int c){ public static int posToChunk(int c){
int chunk = c / 16; return Math.floorDiv(c, 16);
if(c < 0)
chunk--;
return chunk;
} }
@Deprecated @Deprecated
@@ -31,6 +31,10 @@ public interface ProtocolWrapper {
boolean unfilteredTileEntityDataAction(Object packet); boolean unfilteredTileEntityDataAction(Object packet);
default String tileEntityDataType(Object packet) {
return null;
}
BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider); BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider);
BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider); BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider);
@@ -27,6 +27,7 @@ import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@@ -67,11 +68,12 @@ public class TechHider {
private final Set<Integer> obfuscateIds; private final Set<Integer> obfuscateIds;
@Getter @Getter
private final Set<String> hiddenBlockEntities; private final Set<String> hiddenBlockEntities;
private boolean enabled;
public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set<Material> obfuscate, Set<String> hiddenBlockEntities) { public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set<Material> obfuscate, Set<String> hiddenBlockEntities) {
this.locationEvaluator = locationEvaluator; this.locationEvaluator = locationEvaluator;
this.obfuscateIds = obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet()); this.obfuscateIds = obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet());
this.hiddenBlockEntities = hiddenBlockEntities; this.hiddenBlockEntities = hiddenBlockEntities.stream().map(TechHider::normalizeBlockEntityId).collect(Collectors.toSet());
this.obfuscationTarget = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget)); this.obfuscationTarget = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget));
this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget); this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget);
@@ -81,9 +83,11 @@ public class TechHider {
techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this)); techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this));
techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this)); techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this));
if(Core.getVersion() > 12 && Core.getVersion() < 19) { if(Core.getVersion() > 12 && Core.getVersion() < 19 || Core.getVersion() >= 21) {
Class<?> blockBreakClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket"); Class<?> blockBreakClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket");
techhiders.put(blockBreakClass, ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this)); BiFunction<Player, Object, Object> blockBreakHider = ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this);
if(blockBreakHider != null)
techhiders.put(blockBreakClass, blockBreakHider);
} }
if(Core.getVersion() > 8){ if(Core.getVersion() > 8){
@@ -94,10 +98,18 @@ public class TechHider {
} }
public void enable() { public void enable() {
if(enabled)
return;
enabled = true;
techhiders.forEach(TinyProtocol.instance::addFilter); techhiders.forEach(TinyProtocol.instance::addFilter);
} }
public void disable() { public void disable() {
if(!enabled)
return;
enabled = false;
techhiders.forEach(TinyProtocol.instance::removeFilter); techhiders.forEach(TinyProtocol.instance::removeFilter);
} }
@@ -142,6 +154,8 @@ public class TechHider {
case SKIP: case SKIP:
return packet; return packet;
case CHECK: case CHECK:
if(isHiddenBlockEntity(ProtocolWrapper.impl.tileEntityDataType(packet)))
return null;
if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet))
return packet; return packet;
default: default:
@@ -149,6 +163,19 @@ public class TechHider {
} }
} }
public boolean isHiddenBlockEntity(String id) {
return hiddenBlockEntities.contains(normalizeBlockEntityId(id));
}
public static String normalizeBlockEntityId(String id) {
if(id == null)
return "";
id = id.toLowerCase(Locale.ROOT);
int namespaceSeparator = id.indexOf(':');
return namespaceSeparator < 0 ? id : id.substring(namespaceSeparator + 1);
}
public enum State { public enum State {
SKIP, SKIP,
CHECK, CHECK,
@@ -50,7 +50,7 @@ public class WhoisCommand extends SWCommand {
@Register(description = "WHOIS_USAGE") @Register(description = "WHOIS_USAGE")
public void whois(Chatter sender, long id, WhoisParameterTypes... parameters) { public void whois(Chatter sender, long id, WhoisParameterTypes... parameters) {
if(!sender.user().hasPerm(UserPerm.ADMINISTRATION)) { if(!sender.user().hasPerm(UserPerm.ADMINISTRATION) && !sender.user().hasPerm(UserPerm.PREFIX_DEVELOPER)) {
sender.system("UNKNOWN_PLAYER"); sender.system("UNKNOWN_PLAYER");
return; return;
} }