Merge branch 'main' into FightSystem/fix-tech-and-hull-hider

This commit is contained in:
D4rkr34lm
2026-05-17 19:58:16 +02:00
968 changed files with 14917 additions and 33414 deletions
+23 -3
View File
@@ -25,6 +25,23 @@ tasks.compileJava {
options.isWarnings = false
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.addAll(
listOf(
"--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED"
)
)
}
tasks.withType<Test>().configureEach {
jvmArgs("--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED")
}
dependencies {
compileOnly(libs.classindex)
annotationProcessor(libs.classindex)
@@ -32,11 +49,14 @@ dependencies {
compileOnly(project(":CommandFramework", "default"))
compileOnly(project(":SpigotCore:CRIUDummy", "default"))
compileOnly(libs.worldedit12)
compileOnly(libs.fawe)
compileOnly(libs.spigotapi)
compileOnly(libs.netty)
compileOnly(libs.paperapi)
compileOnly(libs.nms)
compileOnly(libs.authlib)
compileOnly(libs.datafixer)
compileOnly(libs.netty)
compileOnly(libs.brigadier)
compileOnly(libs.fastutil)
compileOnly(libs.nms21)
@@ -16,7 +16,7 @@
# 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/>.
#
LOCAL_CHAT=§eLocal §r{0}§8»§7 {1}
LOCAL_CHAT = §eLocal §r{0}§8»§7 {1}
COMMAND_SYSTEM_ERROR = §cError executing the command!
@@ -25,86 +25,86 @@ SWLISINV_NEXT_PAGE_INACTIVE = §7Next page
SWLISINV_PREVIOUS_PAGE_ACTIVE = §ePrevious page
SWLISINV_PREVIOUS_PAGE_INACTIVE = §7Previous page
SCHEM_SELECTOR_TITLE={0} selection: {1}
SCHEM_SELECTOR_BACK=§eBack
SCHEM_SELECTOR_DIR=§9Directory
SCHEM_SELECTOR_RANK=§8Rank {0}
SCHEM_SELECTOR_OWN=§7Own schematics
SCHEM_SELECTOR_PUB=§7Public schematics
SCHEM_SELECTOR_SEL_DIR=§7Select directory
SCHEM_SELECTOR_NEW_DIR=§7New directory
SCHEM_SELECTOR_FILTER=§7Filter
SCHEM_SELECTOR_SORTING=§7Order by
SCHEM_SELECTOR_SORTING_CURRENT=§7Current: §e{0}
SCHEM_SELECTOR_SORTING_NAME=Name
SCHEM_SELECTOR_SORTING_TYPE=Schematic type
SCHEM_SELECTOR_SORTING_UPDATE=Last update
SCHEM_SELECTOR_SORTING_DIRECTION=§e{0} §7order
SCHEM_SELECTOR_SORTING_ASC=Ascending
SCHEM_SELECTOR_SORTING_DSC=Descending
SCHEM_SELECTOR_CLICK_BACK=§7Click to go back
SCHEM_SELECTOR_TITLE = {0} selection: {1}
SCHEM_SELECTOR_BACK = §eBack
SCHEM_SELECTOR_DIR = §9Directory
SCHEM_SELECTOR_RANK = §8Rank {0}
SCHEM_SELECTOR_OWN = §7Own schematics
SCHEM_SELECTOR_PUB = §7Public schematics
SCHEM_SELECTOR_SEL_DIR = §7Select directory
SCHEM_SELECTOR_NEW_DIR = §7New directory
SCHEM_SELECTOR_FILTER = §7Filter
SCHEM_SELECTOR_SORTING = §7Order by
SCHEM_SELECTOR_SORTING_CURRENT = §7Current: §e{0}
SCHEM_SELECTOR_SORTING_NAME = Name
SCHEM_SELECTOR_SORTING_TYPE = Schematic type
SCHEM_SELECTOR_SORTING_UPDATE = Last update
SCHEM_SELECTOR_SORTING_DIRECTION = §e{0} §7order
SCHEM_SELECTOR_SORTING_ASC = Ascending
SCHEM_SELECTOR_SORTING_DSC = Descending
SCHEM_SELECTOR_CLICK_BACK = §7Click to go back
SCHEM_SELECTOR_ITEM_NAME=§e{0}
SCHEM_SELECTOR_ITEM_NAME_FILTER=§7{0}
SCHEM_SELECTOR_ITEM_REPLACE=§e{0}§7
SCHEM_SELECTOR_ITEM_LORE_TYPE=§7{0}
SCHEM_SELECTOR_ITEM_NAME = §e{0}
SCHEM_SELECTOR_ITEM_NAME_FILTER = §7{0}
SCHEM_SELECTOR_ITEM_REPLACE = §e{0}§7
SCHEM_SELECTOR_ITEM_LORE_TYPE = §7{0}
SCHEM_SELECTOR_CREATE_DIR_TITLE=Create directory
SCHEM_SELECTOR_CREATE_DIR_TITLE = Create directory
SCHEM_SELECTOR_FILTER_TITLE=Filter
SCHEM_SELECTOR_FILTER_ENTER_NAME=Insert name
SCHEM_SELECTOR_FILTER_NAME=§7Search by name...
SCHEM_SELECTOR_FILTER_NAME_SEARCH=§7Search term: §e{0}
SCHEM_SELECTOR_FILTER_ENTER_OWNER=Choose owner
SCHEM_SELECTOR_FILTER_OWNER=§7Search by owner...
SCHEM_SELECTOR_FILTER_OWNER_SEARCH=§7Owner: §e{0}
SCHEM_SELECTOR_FILTER_SEL_TYPE=Choose type...
SCHEM_SELECTOR_FILTER_TYPE=§7Search by type...
SCHEM_SELECTOR_FILTER_TYPE_SEARCH=§7Type: §e{0}
SCHEM_SELECTOR_FILTER_MAT=§7Filter by item...
SCHEM_SELECTOR_FILTER_MAT_SEARCH=§7Item: §e{0}
SCHEM_SELECTOR_CANCEL=§eCancel
SCHEM_SELECTOR_GO=§eSearch...
SCHEM_SELECTOR_SCHEMATIC=Schematic
SCHEM_SELECTOR_DIRECTORY=Directory
SCHEM_SELECTOR_SCHEMATIC_NODE=Schematic/Directory
SCHEM_SELECTOR_FILTER_TITLE = Filter
SCHEM_SELECTOR_FILTER_ENTER_NAME = Insert name
SCHEM_SELECTOR_FILTER_NAME = §7Search by name...
SCHEM_SELECTOR_FILTER_NAME_SEARCH = §7Search term: §e{0}
SCHEM_SELECTOR_FILTER_ENTER_OWNER = Choose owner
SCHEM_SELECTOR_FILTER_OWNER = §7Search by owner...
SCHEM_SELECTOR_FILTER_OWNER_SEARCH = §7Owner: §e{0}
SCHEM_SELECTOR_FILTER_SEL_TYPE = Choose type...
SCHEM_SELECTOR_FILTER_TYPE = §7Search by type...
SCHEM_SELECTOR_FILTER_TYPE_SEARCH = §7Type: §e{0}
SCHEM_SELECTOR_FILTER_MAT = §7Filter by item...
SCHEM_SELECTOR_FILTER_MAT_SEARCH = §7Item: §e{0}
SCHEM_SELECTOR_CANCEL = §eCancel
SCHEM_SELECTOR_GO = §eSearch...
SCHEM_SELECTOR_SCHEMATIC = Schematic
SCHEM_SELECTOR_DIRECTORY = Directory
SCHEM_SELECTOR_SCHEMATIC_NODE = Schematic/Directory
SCHEM_SELECTOR_FILTER_TITLE_SINGLE=§eSingle Filter
SCHEM_SELECTOR_FILTER_TITLE_MULTI=§e{0} Filters
SCHEM_SELECTOR_FILTER_TITLE_EMPTY=§eNo Filters
SCHEM_SELECTOR_FILTER_EMPTY=§7Empty
SCHEM_SELECTOR_FILTER_TITLE_SINGLE = §eSingle Filter
SCHEM_SELECTOR_FILTER_TITLE_MULTI = §e{0} Filters
SCHEM_SELECTOR_FILTER_TITLE_EMPTY = §eNo Filters
SCHEM_SELECTOR_FILTER_EMPTY = §7Empty
MATERIAL_SELECTOR_TITLE=Select material
MATERIAL_SELECTOR_TITLE = Select material
BAN_TEAM={0} §e{1} §7was §e§lbanned§7 by §e{2} {3}§8: §f{4}
BAN_PERMA=§7You are §e§lbanned §epermanently§8: §e{0}
BAN_UNTIL=§7You are §e§lbanned §euntil {0}§8: §e{1}
UNBAN_ERROR=§cThe player isn't banned.
UNBAN=§7You have §e§lunbanned §e{0}.
BAN_TEAM = {0} §e{1} §7was §e§lbanned§7 by §e{2} {3}§8: §f{4}
BAN_PERMA = §7You are §e§lbanned §epermanently§8: §e{0}
BAN_UNTIL = §7You are §e§lbanned §euntil {0}§8: §e{1}
UNBAN_ERROR = §cThe player isn't banned.
UNBAN = §7You have §e§lunbanned §e{0}.
MUTE_TEAM={0} §e{1} §7was §e§lmuted§7 by §e{2} {3}§8: §f{4}
MUTE_PERMA=§7You are §epermanently §e§lmuted§8: §e{0}
MUTE_UNTIL=§7You are §e§lmuted §euntil {0}§8: §e{1}
UNMUTE_ERROR=§cThe player isn't muted.
UNMUTE=§7You have §e§lunmuted §e{0}.
MUTE_TEAM = {0} §e{1} §7was §e§lmuted§7 by §e{2} {3}§8: §f{4}
MUTE_PERMA = §7You are §epermanently §e§lmuted§8: §e{0}
MUTE_UNTIL = §7You are §e§lmuted §euntil {0}§8: §e{1}
UNMUTE_ERROR = §cThe player isn't muted.
UNMUTE = §7You have §e§lunmuted §e{0}.
NOSCHEMRECEIVING_TEAM={0} §e{1} §7was excluded from §e{2} {3} §7from §e§lrecieving schematics§8: §f{4}
NOSCHEMRECEIVING_PERMA=§7You are §epermanently§7 excluded from receiving §e§lschematics§8: §e{0}
NOSCHEMRECEIVING_UNTIL=§7You are excluded from receiving §e§lschematics §euntil {0}§8: §e{1}
UNNOSCHEMRECEIVING_ERROR=§cThe player is not excluded from receiving schematics.
UNNOSCHEMRECEIVING=§e{0} §7may now receive §e§lschematics§7 again§8.
NOSCHEMRECEIVING_TEAM = {0} §e{1} §7was excluded from §e{2} {3} §7from §e§lrecieving schematics§8: §f{4}
NOSCHEMRECEIVING_PERMA = §7You are §epermanently§7 excluded from receiving §e§lschematics§8: §e{0}
NOSCHEMRECEIVING_UNTIL = §7You are excluded from receiving §e§lschematics §euntil {0}§8: §e{1}
UNNOSCHEMRECEIVING_ERROR = §cThe player is not excluded from receiving schematics.
UNNOSCHEMRECEIVING = §e{0} §7may now receive §e§lschematics§7 again§8.
NOSCHEMSHARING_TEAM={0} §e{1} §7was excluded from §e{2} {3} §7from §e§lsharing schematics§8: §f{4}
NOSCHEMSHARING_PERMA=§7You are §epermanently§7 excluded from sharing §e§lschematics§8: §e{0}
NOSCHEMSHARING_UNTIL=§7You are excluded from sharing §e§lschematics §euntil {0}§8: §e{1}
UNNOSCHEMSHARING_ERROR=§cThe player is not excluded from sharing schematics.
UNNOSCHEMSHARING=§e{0} §7may now share §e§lschematics§7 again§8.
NOSCHEMSHARING_TEAM = {0} §e{1} §7was excluded from §e{2} {3} §7from §e§lsharing schematics§8: §f{4}
NOSCHEMSHARING_PERMA = §7You are §epermanently§7 excluded from sharing §e§lschematics§8: §e{0}
NOSCHEMSHARING_UNTIL = §7You are excluded from sharing §e§lschematics §euntil {0}§8: §e{1}
UNNOSCHEMSHARING_ERROR = §cThe player is not excluded from sharing schematics.
UNNOSCHEMSHARING = §e{0} §7may now share §e§lschematics§7 again§8.
NOSCHEMSUBMITTING_TEAM={0} §e{1} §7was excluded from §e{2} {3} §7from §e§lsubmitting schematics§8: §f{4}
NOSCHEMSUBMITTING_PERMA=§7You are §epermanently§7 excluded from submitting §e§lschematics§8: §e{0}
NOSCHEMSUBMITTING_UNTIL=§7You are excluded from submitting §e§lschematics §euntil {0}§8: §e{1}
UNNOSCHEMSUBMITTING_ERROR=§cThe player is not excluded from submitting schematics.
UNNOSCHEMSUBMITTING=§e{0} §7may now submit §e§lschematics§7 again§8.
NOSCHEMSUBMITTING_TEAM = {0} §e{1} §7was excluded from §e{2} {3} §7from §e§lsubmitting schematics§8: §f{4}
NOSCHEMSUBMITTING_PERMA = §7You are §epermanently§7 excluded from submitting §e§lschematics§8: §e{0}
NOSCHEMSUBMITTING_UNTIL = §7You are excluded from submitting §e§lschematics §euntil {0}§8: §e{1}
UNNOSCHEMSUBMITTING_ERROR = §cThe player is not excluded from submitting schematics.
UNNOSCHEMSUBMITTING = §e{0} §7may now submit §e§lschematics§7 again§8.
WORLDEDIT_CUI_TITLE = WorldEdit CUI
WORLDEDIT_CUI_TITLE_SUBMENU = WorldEdit CUI - {0}
@@ -16,7 +16,7 @@
# 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/>.
#
LOCAL_CHAT=§eLokal §r{0}§8»§7 {1}
LOCAL_CHAT = §eLokal §r{0}§8»§7 {1}
COMMAND_SYSTEM_ERROR = §cFehler beim Ausführen des Befehls!
@@ -25,81 +25,81 @@ SWLISINV_NEXT_PAGE_INACTIVE = §7Seite vor
SWLISINV_PREVIOUS_PAGE_ACTIVE = §eSeite zurück
SWLISINV_PREVIOUS_PAGE_INACTIVE = §7Seite zurück
SCHEM_SELECTOR_TITLE={0} auswählen: {1}
SCHEM_SELECTOR_BACK=§eZurück
SCHEM_SELECTOR_DIR=§9Ordner
SCHEM_SELECTOR_RANK=§8Rang {0}
SCHEM_SELECTOR_OWN=§7Eigene Schematics
SCHEM_SELECTOR_PUB=§7Public Schematics
SCHEM_SELECTOR_SEL_DIR=§7Ordner auswählen
SCHEM_SELECTOR_NEW_DIR=§7Neuer Ordner
SCHEM_SELECTOR_FILTER=§7Filter
SCHEM_SELECTOR_SORTING=§7Sortierung
SCHEM_SELECTOR_SORTING_CURRENT=§7Aktuell: §e{0}
SCHEM_SELECTOR_SORTING_NAME=Name
SCHEM_SELECTOR_SORTING_TYPE=Schematic-Typ
SCHEM_SELECTOR_SORTING_UPDATE=Letztes Update
SCHEM_SELECTOR_SORTING_DIRECTION=§7Richtung: §e{0}
SCHEM_SELECTOR_SORTING_ASC=Aufsteigend
SCHEM_SELECTOR_SORTING_DSC=Absteigend
SCHEM_SELECTOR_CLICK_BACK=§7Klicke hier, um zurückzugehen
SCHEM_SELECTOR_TITLE = {0} auswählen: {1}
SCHEM_SELECTOR_BACK = §eZurück
SCHEM_SELECTOR_DIR = §9Ordner
SCHEM_SELECTOR_RANK = §8Rang {0}
SCHEM_SELECTOR_OWN = §7Eigene Schematics
SCHEM_SELECTOR_PUB = §7Public Schematics
SCHEM_SELECTOR_SEL_DIR = §7Ordner auswählen
SCHEM_SELECTOR_NEW_DIR = §7Neuer Ordner
SCHEM_SELECTOR_FILTER = §7Filter
SCHEM_SELECTOR_SORTING = §7Sortierung
SCHEM_SELECTOR_SORTING_CURRENT = §7Aktuell: §e{0}
SCHEM_SELECTOR_SORTING_NAME = Name
SCHEM_SELECTOR_SORTING_TYPE = Schematic-Typ
SCHEM_SELECTOR_SORTING_UPDATE = Letztes Update
SCHEM_SELECTOR_SORTING_DIRECTION = §7Richtung: §e{0}
SCHEM_SELECTOR_SORTING_ASC = Aufsteigend
SCHEM_SELECTOR_SORTING_DSC = Absteigend
SCHEM_SELECTOR_CLICK_BACK = §7Klicke hier, um zurückzugehen
SCHEM_SELECTOR_CREATE_DIR_TITLE=Ordner erstellen
SCHEM_SELECTOR_CREATE_DIR_TITLE = Ordner erstellen
SCHEM_SELECTOR_FILTER_TITLE=Filter
SCHEM_SELECTOR_FILTER_ENTER_NAME=Name eingeben
SCHEM_SELECTOR_FILTER_NAME=§7Nach Namen suchen...
SCHEM_SELECTOR_FILTER_NAME_SEARCH=§7Suchbegriff: §e{0}
SCHEM_SELECTOR_FILTER_ENTER_OWNER=Besitzer eingeben
SCHEM_SELECTOR_FILTER_OWNER=§7Nach Besitzer suchen...
SCHEM_SELECTOR_FILTER_OWNER_SEARCH=§7Besitzer: §e{0}
SCHEM_SELECTOR_FILTER_SEL_TYPE=Typ wählen...
SCHEM_SELECTOR_FILTER_TYPE=§7Nach Typ filtern...
SCHEM_SELECTOR_FILTER_TYPE_SEARCH=§7Typ: §e{0}
SCHEM_SELECTOR_FILTER_MAT=§7Nach Item filtern...
SCHEM_SELECTOR_FILTER_MAT_SEARCH=§7Item: §e{0}
SCHEM_SELECTOR_CANCEL=§eAbbrechen
SCHEM_SELECTOR_GO=§eSuchen...
SCHEM_SELECTOR_SCHEMATIC=Schematic
SCHEM_SELECTOR_DIRECTORY=Ordner
SCHEM_SELECTOR_SCHEMATIC_NODE=Schematic/Ordner
SCHEM_SELECTOR_FILTER_TITLE = Filter
SCHEM_SELECTOR_FILTER_ENTER_NAME = Name eingeben
SCHEM_SELECTOR_FILTER_NAME = §7Nach Namen suchen...
SCHEM_SELECTOR_FILTER_NAME_SEARCH = §7Suchbegriff: §e{0}
SCHEM_SELECTOR_FILTER_ENTER_OWNER = Besitzer eingeben
SCHEM_SELECTOR_FILTER_OWNER = §7Nach Besitzer suchen...
SCHEM_SELECTOR_FILTER_OWNER_SEARCH = §7Besitzer: §e{0}
SCHEM_SELECTOR_FILTER_SEL_TYPE = Typ wählen...
SCHEM_SELECTOR_FILTER_TYPE = §7Nach Typ filtern...
SCHEM_SELECTOR_FILTER_TYPE_SEARCH = §7Typ: §e{0}
SCHEM_SELECTOR_FILTER_MAT = §7Nach Item filtern...
SCHEM_SELECTOR_FILTER_MAT_SEARCH = §7Item: §e{0}
SCHEM_SELECTOR_CANCEL = §eAbbrechen
SCHEM_SELECTOR_GO = §eSuchen...
SCHEM_SELECTOR_SCHEMATIC = Schematic
SCHEM_SELECTOR_DIRECTORY = Ordner
SCHEM_SELECTOR_SCHEMATIC_NODE = Schematic/Ordner
SCHEM_SELECTOR_FILTER_TITLE_SINGLE=§eEinzelfilter
SCHEM_SELECTOR_FILTER_TITLE_MULTI=§e{0} §e§lMehrfachfilter
SCHEM_SELECTOR_FILTER_TITLE_EMPTY=§eKeine Filter
SCHEM_SELECTOR_FILTER_EMPTY=§7Leer
SCHEM_SELECTOR_FILTER_TITLE_SINGLE = §eEinzelfilter
SCHEM_SELECTOR_FILTER_TITLE_MULTI = §e{0} §e§lMehrfachfilter
SCHEM_SELECTOR_FILTER_TITLE_EMPTY = §eKeine Filter
SCHEM_SELECTOR_FILTER_EMPTY = §7Leer
MATERIAL_SELECTOR_TITLE=Material auswählen
MATERIAL_SELECTOR_TITLE = Material auswählen
BAN_TEAM={0} §e{1} §7wurde von §e{2} {3} §e§lgebannt§8: §f{4}
BAN_PERMA=§7Du bist §epermanent §e§lgebannt§8: §e{0}
BAN_UNTIL=§7Du bist §ebis zum {0} §e§lgebannt§8: §e{1}
UNBAN_ERROR=§cDer Spieler ist nicht gebannt.
UNBAN=§7Du hast §e{0} §e§lentbannt.
BAN_TEAM = {0} §e{1} §7wurde von §e{2} {3} §e§lgebannt§8: §f{4}
BAN_PERMA = §7Du bist §epermanent §e§lgebannt§8: §e{0}
BAN_UNTIL = §7Du bist §ebis zum {0} §e§lgebannt§8: §e{1}
UNBAN_ERROR = §cDer Spieler ist nicht gebannt.
UNBAN = §7Du hast §e{0} §e§lentbannt.
MUTE_TEAM={0} §e{1} §7wurde von §e{2} {3} §e§lgemuted§8: §f{4}
MUTE_PERMA=§7Du bist §epermanent §e§lgemuted§8: §e{0}
MUTE_UNTIL=§7Du bist §ebis zum {0} §e§lgemuted§8: §e{1}
UNMUTE_ERROR=§cDer Spieler ist nicht gemuted.
UNMUTE=§7Du hast §e{0} §e§lentmuted.
MUTE_TEAM = {0} §e{1} §7wurde von §e{2} {3} §e§lgemuted§8: §f{4}
MUTE_PERMA = §7Du bist §epermanent §e§lgemuted§8: §e{0}
MUTE_UNTIL = §7Du bist §ebis zum {0} §e§lgemuted§8: §e{1}
UNMUTE_ERROR = §cDer Spieler ist nicht gemuted.
UNMUTE = §7Du hast §e{0} §e§lentmuted.
NOSCHEMRECEIVING_TEAM={0} §e{1} §7wurde von §e{2} {3} §7vom §e§lSchematicerhalten§7 ausgeschlossen§8: §f{4}
NOSCHEMRECEIVING_PERMA=§7Du bist §epermanent §7vom Erhalten von §e§lSchematics§7 ausgeschlossen§8: §e{0}
NOSCHEMRECEIVING_UNTIL=§7Du bist §ebis zum {0} §7vom Erhalten von §e§lSchematics§7 ausgeschlossen§8: §e{1}
UNNOSCHEMRECEIVING_ERROR=§cDer Spieler ist nicht vom Erhalten von Schematics ausgeschlossen.
UNNOSCHEMRECEIVING=§e{0} §7darf nun wieder §e§lSchematics§7 erhalten§8.
NOSCHEMRECEIVING_TEAM = {0} §e{1} §7wurde von §e{2} {3} §7vom §e§lSchematicerhalten§7 ausgeschlossen§8: §f{4}
NOSCHEMRECEIVING_PERMA = §7Du bist §epermanent §7vom Erhalten von §e§lSchematics§7 ausgeschlossen§8: §e{0}
NOSCHEMRECEIVING_UNTIL = §7Du bist §ebis zum {0} §7vom Erhalten von §e§lSchematics§7 ausgeschlossen§8: §e{1}
UNNOSCHEMRECEIVING_ERROR = §cDer Spieler ist nicht vom Erhalten von Schematics ausgeschlossen.
UNNOSCHEMRECEIVING = §e{0} §7darf nun wieder §e§lSchematics§7 erhalten§8.
NOSCHEMSHARING_TEAM={0} §e{1} §7wurde von §e{2} {3} §7vom §e§lSchematicverteilen§7 ausgeschlossen§8: §f{4}
NOSCHEMSHARING_PERMA=§7Du bist §epermanent §7vom §e§lVerteilen von Schematics§7 ausgeschlossen§8: §e{0}
NOSCHEMSHARING_UNTIL=§7Du bist §ebis zum {0} §7vom §e§lVerteilen von Schematics§7 ausgeschlossen§8: §e{1}
UNNOSCHEMSHARING_ERROR=§cDer Spieler ist nicht vom Verteilen von Schematics ausgeschlossen.
UNNOSCHEMSHARING=§e{0} §7darf nun wieder §e§lSchematics§7 verteilen§8.
NOSCHEMSHARING_TEAM = {0} §e{1} §7wurde von §e{2} {3} §7vom §e§lSchematicverteilen§7 ausgeschlossen§8: §f{4}
NOSCHEMSHARING_PERMA = §7Du bist §epermanent §7vom §e§lVerteilen von Schematics§7 ausgeschlossen§8: §e{0}
NOSCHEMSHARING_UNTIL = §7Du bist §ebis zum {0} §7vom §e§lVerteilen von Schematics§7 ausgeschlossen§8: §e{1}
UNNOSCHEMSHARING_ERROR = §cDer Spieler ist nicht vom Verteilen von Schematics ausgeschlossen.
UNNOSCHEMSHARING = §e{0} §7darf nun wieder §e§lSchematics§7 verteilen§8.
NOSCHEMSUBMITTING_TEAM={0} §e{1} §7wurde von §e{2} {3} §7vom §e§lSchematiceinsenden§7 ausgeschlossen§8: §f{4}
NOSCHEMSUBMITTING_PERMA=§7Du bist §epermanent §7vom §e§lEinsenden von Schematics§7 ausgeschlossen§8: §e{0}
NOSCHEMSUBMITTING_UNTIL=§7Du bist §ebis zum {0} §7vom §e§lEinsenden von Schematics§7 ausgeschlossen§8: §e{1}
UNNOSCHEMSUBMITTING_ERROR=§cDer Spieler ist nicht vom Einsenden von Schematics ausgeschlossen.
UNNOSCHEMSUBMITTING=§e{0} §7darf nun wieder §e§lSchematis§7 einsenden§8.
NOSCHEMSUBMITTING_TEAM = {0} §e{1} §7wurde von §e{2} {3} §7vom §e§lSchematiceinsenden§7 ausgeschlossen§8: §f{4}
NOSCHEMSUBMITTING_PERMA = §7Du bist §epermanent §7vom §e§lEinsenden von Schematics§7 ausgeschlossen§8: §e{0}
NOSCHEMSUBMITTING_UNTIL = §7Du bist §ebis zum {0} §7vom §e§lEinsenden von Schematics§7 ausgeschlossen§8: §e{1}
UNNOSCHEMSUBMITTING_ERROR = §cDer Spieler ist nicht vom Einsenden von Schematics ausgeschlossen.
UNNOSCHEMSUBMITTING = §e{0} §7darf nun wieder §e§lSchematis§7 einsenden§8.
WORLDEDIT_CUI_SELECTION = Eigene Auswahl
WORLDEDIT_CUI_CLIPBOARD = Eigene Kopie
@@ -1,25 +1,15 @@
package com.comphenix.tinyprotocol;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import com.google.common.collect.MapMaker;
import de.steamwar.Reflection;
import de.steamwar.core.Core;
import io.netty.channel.*;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.login.ServerboundHelloPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerConnectionListener;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -40,33 +30,10 @@ import com.mojang.authlib.GameProfile;
* Represents a very tiny alternative to ProtocolLib.
* <p>
* It now supports intercepting packets during login and status ping (such as OUT_SERVER_PING)!
*
*
* @author Kristian
*/
public abstract class TinyProtocol {
private static final AtomicInteger ID = new AtomicInteger(0);
// Required Minecraft classes
private static final Class<?> entityPlayerClass = Reflection.getClass("{nms}.EntityPlayer", "net.minecraft.server.level.EntityPlayer");
private static final Class<?> playerConnectionClass = Reflection.getClass("{nms}.PlayerConnection", "net.minecraft.server.network.PlayerConnection");
private static final Class<?> networkManagerClass = Reflection.getClass("{nms}.NetworkManager", "net.minecraft.network.NetworkManager");
// Used in order to lookup a channel
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
private static final FieldAccessor<?> getConnection = Reflection.getField(entityPlayerClass, null, playerConnectionClass);
private static final FieldAccessor<?> getManager = Reflection.getField(playerConnectionClass, null, networkManagerClass);
private static final FieldAccessor<Channel> getChannel = Reflection.getField(networkManagerClass, Channel.class, 0);
// Looking up ServerConnection
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer", "net.minecraft.server.MinecraftServer");
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection", "net.minecraft.server.network.ServerConnection");
private static final FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
private static final FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
// Packets we have to intercept
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getClass("{nms}.PacketLoginInStart", "net.minecraft.network.protocol.login.PacketLoginInStart");
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
public class TinyProtocol {
// Speedup channel lookup
private Map<String, Channel> channelLookup = new MapMaker().weakValues().makeMap();
private Listener listener;
@@ -75,7 +42,7 @@ public abstract class TinyProtocol {
private Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().<Channel, Boolean>makeMap());
// List of network markers
private List<Object> networkManagers;
public List<Connection> networkManagers;
// Injected channel handlers
private List<Channel> serverChannels = new ArrayList<>();
@@ -84,24 +51,28 @@ public abstract class TinyProtocol {
private ChannelInitializer<Channel> endInitProtocol;
// Current handler name
private String handlerName;
private static final String HANDLER_NAME = "tiny-steamwar";
protected volatile boolean closed;
protected Plugin plugin;
public static final TinyProtocol instance = new TinyProtocol(Core.getInstance());
private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new HashMap<>();
public static void init() {
// enforce init
}
/**
* Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients.
* <p>
* You can construct multiple instances per plugin.
*
*
* @param plugin - the plugin.
*/
public TinyProtocol(final Plugin plugin) {
private TinyProtocol(final Plugin plugin) {
this.plugin = plugin;
// Compute handler name
this.handlerName = getHandlerName();
// Prepare existing players
registerBukkitEvents();
@@ -155,7 +126,7 @@ public abstract class TinyProtocol {
};
serverChannelHandler = new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel channel = (Channel) msg;
@@ -176,8 +147,7 @@ public abstract class TinyProtocol {
@EventHandler(priority = EventPriority.LOWEST)
public final void onPlayerLogin(PlayerLoginEvent e) {
if (closed)
return;
if (closed) return;
Channel channel = getChannel(e.getPlayer());
@@ -201,35 +171,18 @@ public abstract class TinyProtocol {
@SuppressWarnings("unchecked")
private void registerChannelHandler() {
Object mcServer = getMinecraftServer.get(Bukkit.getServer());
Object serverConnection = getServerConnection.get(mcServer);
boolean looking = true;
try {
Field field = Reflection.getParameterizedField(serverConnectionClass, List.class, networkManagerClass);
field.setAccessible(true);
networkManagers = (List<Object>) field.get(serverConnection);
} catch (Exception ex) {
plugin.getLogger().info("Encountered an exception checking list fields" + ex);
MethodInvoker method = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
networkManagers = (List<Object>) method.invoke(null, serverConnection);
}
if (networkManagers == null) {
throw new IllegalArgumentException("Failed to obtain list of network managers");
}
ServerConnectionListener serverConnection = MinecraftServer.getServer().getConnection();
networkManagers = serverConnection.getConnections();
// We need to synchronize against this list
createServerChannelHandler();
// Find the correct list, or implicitly throw an exception
boolean looking = true;
for (int i = 0; looking; i++) {
List<Object> list = Reflection.getField(serverConnection.getClass(), List.class, i).get(serverConnection);
for (Object item : list) {
if (!ChannelFuture.class.isInstance(item))
break;
if (!(item instanceof ChannelFuture)) break;
// Channel future that contains the server connection
Channel serverChannel = ((ChannelFuture) item).channel();
@@ -242,8 +195,7 @@ public abstract class TinyProtocol {
}
private void unregisterChannelHandler() {
if (serverChannelHandler == null)
return;
if (serverChannelHandler == null) return;
for (Channel serverChannel : serverChannels) {
final ChannelPipeline pipeline = serverChannel.pipeline();
@@ -270,98 +222,71 @@ public abstract class TinyProtocol {
}
}
/**
* Invoked when the server is starting to send a packet to a player.
* <p>
* Note that this is not executed on the main thread.
*
* @param receiver - the receiving player, NULL for early login/status packets.
* @param channel - the channel that received the packet. Never NULL.
* @param packet - the packet being sent.
* @return The packet to send instead, or NULL to cancel the transmission.
*/
public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) {
return packet;
public <T> void addTypedFilter(Class<T> packetType, BiFunction<Player, ? super T, Object> filter) {
packetFilters.computeIfAbsent(packetType, c -> new CopyOnWriteArrayList<>()).add((BiFunction) filter);
}
/**
* Invoked when the server has received a packet from a given player.
* <p>
* Use {@link Channel#remoteAddress()} to get the remote address of the client.
*
* @param sender - the player that sent the packet, NULL for early login/status packets.
* @param channel - channel that received the packet. Never NULL.
* @param packet - the packet being received.
* @return The packet to recieve instead, or NULL to cancel.
*/
public Object onPacketInAsync(Player sender, Channel channel, Object packet) {
return packet;
@Deprecated
public void addFilter(Class<?> packetType, BiFunction<Player, Object, Object> filter) {
packetFilters.computeIfAbsent(packetType, c -> new CopyOnWriteArrayList<>()).add(filter);
}
public void removeFilter(Class<?> packetType, BiFunction<Player, ?, Object> filter) {
packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter);
}
/**
* Send a packet to a particular player.
* <p>
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
*
*
* @param player - the destination player.
* @param packet - the packet to send.
*/
public void sendPacket(Player player, Object packet) {
public void sendPacket(Player player, Packet<?> packet) {
sendPacket(getChannel(player), packet);
}
@Deprecated
public void sendPacket(Player player, Object object) {
if (object instanceof Packet<?> packet) {
sendPacket(getChannel(player), packet);
}
}
/**
* Send a packet to a particular client.
* <p>
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
*
*
* @param channel - client identified by a channel.
* @param packet - the packet to send.
* @param packet - the packet to send.
*/
public void sendPacket(Channel channel, Object packet) {
public void sendPacket(Channel channel, Packet<?> packet) {
channel.pipeline().writeAndFlush(packet);
}
/**
* Pretend that a given packet has been received from a player.
* <p>
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
*
*
* @param player - the player that sent the packet.
* @param packet - the packet that will be received by the server.
*/
public void receivePacket(Player player, Object packet) {
public void receivePacket(Player player, Packet<?> packet) {
receivePacket(getChannel(player), packet);
}
/**
* Pretend that a given packet has been received from a given client.
* <p>
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
*
*
* @param channel - client identified by a channel.
* @param packet - the packet that will be received by the server.
* @param packet - the packet that will be received by the server.
*/
public void receivePacket(Channel channel, Object packet) {
public void receivePacket(Channel channel, Packet<?> packet) {
channel.pipeline().context("encoder").fireChannelRead(packet);
}
/**
* Retrieve the name of the channel injector, default implementation is "tiny-" + plugin name + "-" + a unique ID.
* <p>
* Note that this method will only be invoked once. It is no longer necessary to override this to support multiple instances.
*
* @return A unique channel handler name.
*/
protected String getHandlerName() {
return "tiny-" + plugin.getName() + "-" + ID.incrementAndGet();
}
/**
* Add a custom channel handler to the given player's channel pipeline, allowing us to intercept sent and received packets.
* <p>
* This will automatically be called when a player has logged in.
*
*
* @param player - the player to inject.
*/
public void injectPlayer(Player player) {
@@ -370,7 +295,7 @@ public abstract class TinyProtocol {
/**
* Add a custom channel handler to the given channel.
*
*
* @param channel - the channel to inject.
* @return The intercepted channel, or NULL if it has already been injected.
*/
@@ -380,31 +305,31 @@ public abstract class TinyProtocol {
/**
* Add a custom channel handler to the given channel.
*
*
* @param channel - the channel to inject.
* @return The packet interceptor.
*/
private PacketInterceptor injectChannelInternal(Channel channel) {
try {
PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(handlerName);
PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(HANDLER_NAME);
// Inject our packet interceptor
if (interceptor == null) {
interceptor = new PacketInterceptor();
channel.pipeline().addBefore("packet_handler", handlerName, interceptor);
channel.pipeline().addBefore("packet_handler", HANDLER_NAME, interceptor);
uninjectedChannels.remove(channel);
}
return interceptor;
} catch (IllegalArgumentException e) {
// Try again
return (PacketInterceptor) channel.pipeline().get(handlerName);
return (PacketInterceptor) channel.pipeline().get(HANDLER_NAME);
}
}
/**
* Retrieve the Netty channel associated with a player. This is cached.
*
*
* @param player - the player.
* @return The Netty channel.
*/
@@ -413,10 +338,8 @@ public abstract class TinyProtocol {
// Lookup channel again
if (channel == null) {
Object connection = getConnection.get(getPlayerHandle.invoke(player));
Object manager = getManager.get(connection);
channelLookup.put(player.getName(), channel = getChannel.get(manager));
Channel playerChannel = ((CraftPlayer) player).getHandle().connection.connection.channel;
channelLookup.put(player.getName(), channel = playerChannel);
}
return channel;
@@ -424,7 +347,7 @@ public abstract class TinyProtocol {
/**
* Uninject a specific player.
*
*
* @param player - the injected player.
*/
public void uninjectPlayer(Player player) {
@@ -435,7 +358,7 @@ public abstract class TinyProtocol {
* Uninject a specific channel.
* <p>
* This will also disable the automatic channel injection that occurs when a player has properly logged in.
*
*
* @param channel - the injected channel.
*/
public void uninjectChannel(final Channel channel) {
@@ -449,7 +372,7 @@ public abstract class TinyProtocol {
@Override
public void run() {
channel.pipeline().remove(handlerName);
channel.pipeline().remove(HANDLER_NAME);
}
});
@@ -457,7 +380,7 @@ public abstract class TinyProtocol {
/**
* Determine if the given player has been injected by TinyProtocol.
*
*
* @param player - the player.
* @return TRUE if it is, FALSE otherwise.
*/
@@ -467,12 +390,12 @@ public abstract class TinyProtocol {
/**
* Determine if the given channel has been injected by TinyProtocol.
*
*
* @param channel - the channel.
* @return TRUE if it is, FALSE otherwise.
*/
public boolean hasInjected(Channel channel) {
return channel.pipeline().get(handlerName) != null;
return channel.pipeline().get(HANDLER_NAME) != null;
}
/**
@@ -495,7 +418,7 @@ public abstract class TinyProtocol {
/**
* Channel handler that is inserted into the player's channel pipeline, allowing us to intercept sent and received packets.
*
*
* @author Kristian
*/
private final class PacketInterceptor extends ChannelDuplexHandler {
@@ -509,7 +432,7 @@ public abstract class TinyProtocol {
handleLoginStart(channel, msg);
try {
msg = onPacketInAsync(player, channel, msg);
msg = filterPacket(player, msg);
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error in onPacketInAsync().", e);
}
@@ -522,7 +445,7 @@ public abstract class TinyProtocol {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
try {
msg = onPacketOutAsync(player, ctx.channel(), msg);
msg = filterPacket(player, msg);
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e);
}
@@ -533,10 +456,20 @@ public abstract class TinyProtocol {
}
private void handleLoginStart(Channel channel, Object packet) {
if (PACKET_LOGIN_IN_START.isInstance(packet)) {
GameProfile profile = getGameProfile.get(packet);
channelLookup.put(profile.getName(), channel);
if (packet instanceof ServerboundHelloPacket(String name, UUID packetId)) {
channelLookup.put(name, channel);
}
}
private Object filterPacket(Player player, Object packet) {
List<BiFunction<Player, Object, Object>> filters = packetFilters.getOrDefault(packet.getClass(), Collections.emptyList());
for (BiFunction<Player, Object, Object> filter : filters) {
packet = filter.apply(player, packet);
if (packet == null) break;
}
return packet;
}
}
}
@@ -35,301 +35,281 @@ import java.util.Map;
@UtilityClass
public final class Reflection {
public static final int MAJOR_VERSION;
public static final int MINOR_VERSION;
static {
String[] version = Bukkit.getServer().getBukkitVersion().split("-")[0].split("\\.");
MAJOR_VERSION = Integer.parseInt(version[1]);
MINOR_VERSION = version.length > 2 ? Integer.parseInt(version[2]) : 0;
}
public static final int MAJOR_VERSION;
public static final int MINOR_VERSION;
private static final String ORG_BUKKIT_CRAFTBUKKIT = Bukkit.getServer().getClass().getPackage().getName();
public static final String LEGACY_NET_MINECRAFT_SERVER = ORG_BUKKIT_CRAFTBUKKIT.replace("org.bukkit.craftbukkit", "net.minecraft.server");
static {
String[] version = Bukkit.getServer().getBukkitVersion().split("-")[0].split("\\.");
MAJOR_VERSION = Integer.parseInt(version[1]);
MINOR_VERSION = version.length > 2 ? Integer.parseInt(version[2]) : 0;
}
private static final Map<String, String> spigotClassnames = new HashMap<>();
static {
// See https://mappings.dev for complete mappings
spigotClassnames.put("net.minecraft.Util", "net.minecraft.SystemUtils");
private static final String ORG_BUKKIT_CRAFTBUKKIT = Bukkit.getServer().getClass().getPackage().getName();
public static final String LEGACY_NET_MINECRAFT_SERVER = ORG_BUKKIT_CRAFTBUKKIT.replace("org.bukkit.craftbukkit", "net.minecraft.server");
spigotClassnames.put("net.minecraft.core.BlockPos", "net.minecraft.core.BlockPosition");
spigotClassnames.put("net.minecraft.core.DefaultedRegistry", "net.minecraft.core.RegistryBlocks");
spigotClassnames.put("net.minecraft.core.IdMapper", "net.minecraft.core.RegistryBlockID");
spigotClassnames.put("net.minecraft.core.Vec3i", "net.minecraft.core.BaseBlockPosition");
private static final Map<String, String> spigotClassnames = new HashMap<>();
spigotClassnames.put("net.minecraft.nbt.CompoundTag", "net.minecraft.nbt.NBTTagCompound");
static {
// See https://mappings.dev for complete mappings
spigotClassnames.put("net.minecraft.Util", "net.minecraft.SystemUtils");
spigotClassnames.put("net.minecraft.network.Connection", "net.minecraft.network.NetworkManager");
spigotClassnames.put("net.minecraft.core.BlockPos", "net.minecraft.core.BlockPosition");
spigotClassnames.put("net.minecraft.core.DefaultedRegistry", "net.minecraft.core.RegistryBlocks");
spigotClassnames.put("net.minecraft.core.IdMapper", "net.minecraft.core.RegistryBlockID");
spigotClassnames.put("net.minecraft.core.Vec3i", "net.minecraft.core.BaseBlockPosition");
spigotClassnames.put("net.minecraft.network.chat.Component", "net.minecraft.network.chat.IChatBaseComponent");
spigotClassnames.put("net.minecraft.nbt.CompoundTag", "net.minecraft.nbt.NBTTagCompound");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundAddEntityPacket", "net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundAddPlayerPacket", "net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundAnimatePacket", "net.minecraft.network.protocol.game.PacketPlayOutAnimation");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket", "net.minecraft.network.protocol.game.PacketPlayOutBlockBreak");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket", "net.minecraft.network.protocol.game.PacketPlayOutTileEntityData");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutBlockAction");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket", "net.minecraft.network.protocol.game.PacketPlayOutBlockChange");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundContainerClosePacket", "net.minecraft.network.protocol.game.PacketPlayOutCloseWindow");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundEntityEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityStatus");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundExplodePacket", "net.minecraft.network.protocol.game.PacketPlayOutExplosion");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundGameEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutGameStateChange");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo", "net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$a");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundLevelEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutWorldEvent");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket", "net.minecraft.network.protocol.game.PacketPlayOutWorldParticles");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntity");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Pos", "net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutRelEntityMove");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$PosRot", "net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutRelEntityMoveLook");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot", "net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket", "net.minecraft.network.protocol.game.PacketPlayOutOpenSignEditor");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundRotateHeadPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityHeadRotation");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket", "net.minecraft.network.protocol.game.PacketPlayOutMultiBlockChange");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityVelocity");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityEquipment");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetObjectivePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetScorePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSoundPacket", "net.minecraft.network.protocol.game.PacketPlayOutNamedSoundEffect");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundContainerClickPacket", "net.minecraft.network.protocol.game.PacketPlayInWindowClick");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundInteractPacket", "net.minecraft.network.protocol.game.PacketPlayInUseEntity");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundInteractPacket$Action", "net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundInteractPacket$ActionType", "net.minecraft.network.protocol.game.PacketPlayInUseEntity$b");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos", "net.minecraft.network.protocol.game.PacketPlayInFlying$PacketPlayInPosition");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot", "net.minecraft.network.protocol.game.PacketPlayInFlying$PacketPlayInPositionLook");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Rot", "net.minecraft.network.protocol.game.PacketPlayInFlying$PacketPlayInLook");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundPlayerActionPacket", "net.minecraft.network.protocol.game.PacketPlayInBlockDig");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket", "net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundSignUpdatePacket", "net.minecraft.network.protocol.game.PacketPlayInUpdateSign");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundUseItemPacket", "net.minecraft.network.protocol.game.PacketPlayInBlockPlace");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundUseItemOnPacket", "net.minecraft.network.protocol.game.PacketPlayInUseItem");
spigotClassnames.put("net.minecraft.network.Connection", "net.minecraft.network.NetworkManager");
spigotClassnames.put("net.minecraft.network.syncher.EntityDataAccessor", "net.minecraft.network.syncher.DataWatcherObject");
spigotClassnames.put("net.minecraft.network.syncher.EntityDataSerializer", "net.minecraft.network.syncher.DataWatcherSerializer");
spigotClassnames.put("net.minecraft.network.syncher.EntityDataSerializers", "net.minecraft.network.syncher.DataWatcherRegistry");
spigotClassnames.put("net.minecraft.network.syncher.SynchedEntityData$DataItem", "net.minecraft.network.syncher.DataWatcher$Item");
spigotClassnames.put("net.minecraft.network.chat.Component", "net.minecraft.network.chat.IChatBaseComponent");
spigotClassnames.put("net.minecraft.server.ServerScoreboard$Method", "net.minecraft.server.ScoreboardServer$Action");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundAddEntityPacket", "net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundAddPlayerPacket", "net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundAnimatePacket", "net.minecraft.network.protocol.game.PacketPlayOutAnimation");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket", "net.minecraft.network.protocol.game.PacketPlayOutBlockBreak");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket", "net.minecraft.network.protocol.game.PacketPlayOutTileEntityData");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutBlockAction");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket", "net.minecraft.network.protocol.game.PacketPlayOutBlockChange");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundContainerClosePacket", "net.minecraft.network.protocol.game.PacketPlayOutCloseWindow");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundEntityEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityStatus");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundExplodePacket", "net.minecraft.network.protocol.game.PacketPlayOutExplosion");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundGameEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutGameStateChange");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo", "net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$a");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundLevelEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutWorldEvent");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket", "net.minecraft.network.protocol.game.PacketPlayOutWorldParticles");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntity");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Pos", "net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutRelEntityMove");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$PosRot", "net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutRelEntityMoveLook");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot", "net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket", "net.minecraft.network.protocol.game.PacketPlayOutOpenSignEditor");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundRotateHeadPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityHeadRotation");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket", "net.minecraft.network.protocol.game.PacketPlayOutMultiBlockChange");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityVelocity");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityEquipment");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetObjectivePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetScorePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSoundPacket", "net.minecraft.network.protocol.game.PacketPlayOutNamedSoundEffect");
spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundContainerClickPacket", "net.minecraft.network.protocol.game.PacketPlayInWindowClick");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundInteractPacket", "net.minecraft.network.protocol.game.PacketPlayInUseEntity");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundInteractPacket$Action", "net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundInteractPacket$ActionType", "net.minecraft.network.protocol.game.PacketPlayInUseEntity$b");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos", "net.minecraft.network.protocol.game.PacketPlayInFlying$PacketPlayInPosition");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot", "net.minecraft.network.protocol.game.PacketPlayInFlying$PacketPlayInPositionLook");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Rot", "net.minecraft.network.protocol.game.PacketPlayInFlying$PacketPlayInLook");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundPlayerActionPacket", "net.minecraft.network.protocol.game.PacketPlayInBlockDig");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket", "net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundSignUpdatePacket", "net.minecraft.network.protocol.game.PacketPlayInUpdateSign");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundUseItemPacket", "net.minecraft.network.protocol.game.PacketPlayInBlockPlace");
spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundUseItemOnPacket", "net.minecraft.network.protocol.game.PacketPlayInUseItem");
spigotClassnames.put("net.minecraft.server.level.ChunkMap", "net.minecraft.server.level.PlayerChunkMap");
spigotClassnames.put("net.minecraft.server.level.ChunkMap$TrackedEntity", "net.minecraft.server.level.PlayerChunkMap$EntityTracker");
spigotClassnames.put("net.minecraft.server.level.ServerChunkCache", "net.minecraft.server.level.ChunkProviderServer");
spigotClassnames.put("net.minecraft.server.level.ServerLevel", "net.minecraft.server.level.WorldServer");
spigotClassnames.put("net.minecraft.server.level.ServerPlayer", "net.minecraft.server.level.EntityPlayer");
spigotClassnames.put("net.minecraft.network.syncher.EntityDataAccessor", "net.minecraft.network.syncher.DataWatcherObject");
spigotClassnames.put("net.minecraft.network.syncher.EntityDataSerializer", "net.minecraft.network.syncher.DataWatcherSerializer");
spigotClassnames.put("net.minecraft.network.syncher.EntityDataSerializers", "net.minecraft.network.syncher.DataWatcherRegistry");
spigotClassnames.put("net.minecraft.network.syncher.SynchedEntityData$DataItem", "net.minecraft.network.syncher.DataWatcher$Item");
spigotClassnames.put("net.minecraft.server.network.ServerConnectionListener", "net.minecraft.server.network.ServerConnection");
spigotClassnames.put("net.minecraft.server.ServerScoreboard$Method", "net.minecraft.server.ScoreboardServer$Action");
spigotClassnames.put("net.minecraft.world.InteractionHand", "net.minecraft.world.EnumHand");
spigotClassnames.put("net.minecraft.server.level.ChunkMap", "net.minecraft.server.level.PlayerChunkMap");
spigotClassnames.put("net.minecraft.server.level.ChunkMap$TrackedEntity", "net.minecraft.server.level.PlayerChunkMap$EntityTracker");
spigotClassnames.put("net.minecraft.server.level.ServerChunkCache", "net.minecraft.server.level.ChunkProviderServer");
spigotClassnames.put("net.minecraft.server.level.ServerLevel", "net.minecraft.server.level.WorldServer");
spigotClassnames.put("net.minecraft.server.level.ServerPlayer", "net.minecraft.server.level.EntityPlayer");
spigotClassnames.put("net.minecraft.world.entity.EntityType", "net.minecraft.world.entity.EntityTypes");
spigotClassnames.put("net.minecraft.world.entity.Pose", "net.minecraft.world.entity.EntityPose");
spigotClassnames.put("net.minecraft.server.network.ServerConnectionListener", "net.minecraft.server.network.ServerConnection");
spigotClassnames.put("net.minecraft.world.entity.item.PrimedTnt", "net.minecraft.world.entity.item.EntityTNTPrimed");
spigotClassnames.put("net.minecraft.world.InteractionHand", "net.minecraft.world.EnumHand");
spigotClassnames.put("net.minecraft.world.entity.projectile.AbstractArrow", "net.minecraft.world.entity.projectile.EntityArrow");
spigotClassnames.put("net.minecraft.world.entity.EntityType", "net.minecraft.world.entity.EntityTypes");
spigotClassnames.put("net.minecraft.world.entity.Pose", "net.minecraft.world.entity.EntityPose");
spigotClassnames.put("net.minecraft.world.level.GameType", "net.minecraft.world.level.EnumGamemode");
spigotClassnames.put("net.minecraft.world.level.LevelAccessor", "net.minecraft.world.level.GeneratorAccess");
spigotClassnames.put("net.minecraft.world.entity.item.PrimedTnt", "net.minecraft.world.entity.item.EntityTNTPrimed");
spigotClassnames.put("net.minecraft.world.level.block.state.BlockState", "net.minecraft.world.level.block.state.IBlockData");
spigotClassnames.put("net.minecraft.world.level.block.state.StateDefinition", "net.minecraft.world.level.block.state.BlockStateList");
spigotClassnames.put("net.minecraft.world.entity.projectile.AbstractArrow", "net.minecraft.world.entity.projectile.EntityArrow");
spigotClassnames.put("net.minecraft.world.level.chunk.LevelChunk", "net.minecraft.world.level.chunk.Chunk");
spigotClassnames.put("net.minecraft.world.level.GameType", "net.minecraft.world.level.EnumGamemode");
spigotClassnames.put("net.minecraft.world.level.LevelAccessor", "net.minecraft.world.level.GeneratorAccess");
spigotClassnames.put("net.minecraft.world.level.material.FlowingFluid", "net.minecraft.world.level.material.FluidTypeFlowing");
spigotClassnames.put("net.minecraft.world.level.material.Fluids", "net.minecraft.world.level.material.FluidTypes");
spigotClassnames.put("net.minecraft.world.level.material.FluidState", "net.minecraft.world.level.material.Fluid");
spigotClassnames.put("net.minecraft.world.level.block.state.BlockState", "net.minecraft.world.level.block.state.IBlockData");
spigotClassnames.put("net.minecraft.world.level.block.state.StateDefinition", "net.minecraft.world.level.block.state.BlockStateList");
spigotClassnames.put("net.minecraft.world.phys.BlockHitResult", "net.minecraft.world.phys.MovingObjectPositionBlock");
spigotClassnames.put("net.minecraft.world.phys.Vec3", "net.minecraft.world.phys.Vec3D");
spigotClassnames.put("net.minecraft.world.level.chunk.LevelChunk", "net.minecraft.world.level.chunk.Chunk");
spigotClassnames.put("net.minecraft.resources.ResourceLocation", "net.minecraft.resources.MinecraftKey");
spigotClassnames.put("net.minecraft.world.level.material.FlowingFluid", "net.minecraft.world.level.material.FluidTypeFlowing");
spigotClassnames.put("net.minecraft.world.level.material.Fluids", "net.minecraft.world.level.material.FluidTypes");
spigotClassnames.put("net.minecraft.world.level.material.FluidState", "net.minecraft.world.level.material.Fluid");
spigotClassnames.put("net.minecraft.util.ProgressListener", "net.minecraft.util.IProgressUpdate");
}
spigotClassnames.put("net.minecraft.world.phys.BlockHitResult", "net.minecraft.world.phys.MovingObjectPositionBlock");
spigotClassnames.put("net.minecraft.world.phys.Vec3", "net.minecraft.world.phys.Vec3D");
public static Class<?> getClass(String name) {
try {
if(name.startsWith("org.bukkit.craftbukkit")) {
return Class.forName(ORG_BUKKIT_CRAFTBUKKIT + name.substring(22));
} else if(MAJOR_VERSION < 17 && name.startsWith("net.minecraft")) {
return Class.forName(LEGACY_NET_MINECRAFT_SERVER + "." + spigotClassnames.getOrDefault(name, name).split("[.](?=[^.]*$)")[1]);
} else if(MAJOR_VERSION < 21 || MINOR_VERSION < 4) {
return Class.forName(spigotClassnames.getOrDefault(name, name));
} else {
Class<?> clazz = null;
try {
clazz = Class.forName(name);
} catch (ClassNotFoundException e) {}
if (clazz != null && clazz.getName().equals(name)) {
return clazz;
}
spigotClassnames.put("net.minecraft.resources.ResourceLocation", "net.minecraft.resources.MinecraftKey");
try {
return Core.class.getClassLoader().getParent().loadClass(name);
} catch (ClassNotFoundException e) {
if (clazz == null) {
throw e;
}
spigotClassnames.put("net.minecraft.util.ProgressListener", "net.minecraft.util.IProgressUpdate");
}
return clazz;
}
}
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Cannot find " + name, e);
}
}
public static Class<?> getClass(String name) {
try {
if (name.startsWith("org.bukkit.craftbukkit")) {
return Class.forName(ORG_BUKKIT_CRAFTBUKKIT + name.substring(22));
} else if (MAJOR_VERSION < 17 && name.startsWith("net.minecraft")) {
return Class.forName(LEGACY_NET_MINECRAFT_SERVER + "." + spigotClassnames.getOrDefault(name, name).split("[.](?=[^.]*$)")[1]);
} else if (MAJOR_VERSION < 21 || MINOR_VERSION < 4) {
return Class.forName(spigotClassnames.getOrDefault(name, name));
} else {
Class<?> clazz = null;
try {
clazz = Class.forName(name);
} catch (ClassNotFoundException e) {
}
if (clazz != null && clazz.getName().equals(name)) {
return clazz;
}
@AllArgsConstructor
public static class Field<T> {
private final java.lang.reflect.Field f;
try {
return Core.class.getClassLoader().getParent().loadClass(name);
} catch (ClassNotFoundException e) {
if (clazz == null) {
throw e;
}
@SuppressWarnings("unchecked")
public T get(Object target) {
try {
return (T) f.get(target);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot read field", e);
}
}
return clazz;
}
}
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Cannot find " + name, e);
}
}
public void set(Object target, Object value) {
try {
f.set(target, value);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot write field", e);
}
}
}
@AllArgsConstructor
public static class Field<T> {
private final java.lang.reflect.Field f;
public static <T> Field<T> getField(Class<?> target, String name, Class<T> fieldType) {
return getField(target, name, fieldType, 0);
}
@SuppressWarnings("unchecked")
public T get(Object target) {
try {
return (T) f.get(target);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot read field", e);
}
}
public static <T> Field<T> getField(String className, String name, Class<T> fieldType) {
return getField(getClass(className), name, fieldType, 0);
}
public void set(Object target, Object value) {
try {
f.set(target, value);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot write field", e);
}
}
}
public static <T> Field<T> getField(Class<?> target, Class<T> fieldType, int index) {
return getField(target, null, fieldType, index);
}
public static <T> Field<T> getField(Class<?> target, String name, Class<T> fieldType) {
return getField(target, name, fieldType, 0);
}
public static <T> Field<T> getField(String className, Class<T> fieldType, int index) {
return getField(getClass(className), fieldType, index);
}
public static <T> Field<T> getField(Class<?> target, Class<T> fieldType, int index) {
return getField(target, null, fieldType, index);
}
public static <T> Field<T> getField(Class<?> target, Class<T> fieldType, int index, Class<?>... parameters) {
return getField(target, null, fieldType, index, parameters);
}
public static <T> Field<T> getField(Class<?> target, Class<T> fieldType, int index, Class<?>... parameters) {
return getField(target, null, fieldType, index, parameters);
}
private static <T> Field<T> getField(Class<?> target, String name, Class<T> fieldType, int index, Class<?>... parameters) {
for (final java.lang.reflect.Field field : target.getDeclaredFields()) {
if(matching(field, name, fieldType, parameters) && index-- <= 0) {
field.setAccessible(true);
return new Field<>(field);
}
}
private static <T> Field<T> getField(Class<?> target, String name, Class<T> fieldType, int index, Class<?>... parameters) {
for (final java.lang.reflect.Field field : target.getDeclaredFields()) {
if (matching(field, name, fieldType, parameters) && index-- <= 0) {
field.setAccessible(true);
return new Field<>(field);
}
}
// Search in parent classes
if (target.getSuperclass() != null)
return getField(target.getSuperclass(), name, fieldType, index);
// Search in parent classes
if (target.getSuperclass() != null) {
return getField(target.getSuperclass(), name, fieldType, index);
}
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
}
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
}
private static <T> boolean matching(java.lang.reflect.Field field, String name, Class<T> fieldType, Class<?>... parameters) {
if(name != null && !field.getName().equals(name))
return false;
private static <T> boolean matching(java.lang.reflect.Field field, String name, Class<T> fieldType, Class<?>... parameters) {
if (name != null && !field.getName().equals(name)) return false;
if(!fieldType.isAssignableFrom(field.getType()))
return false;
if (!fieldType.isAssignableFrom(field.getType())) return false;
if(parameters.length > 0) {
Type[] arguments = ((ParameterizedType)field.getGenericType()).getActualTypeArguments();
if (parameters.length > 0) {
Type[] arguments = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();
for(int i = 0; i < parameters.length; i++) {
if(arguments[i] instanceof ParameterizedType ? ((ParameterizedType) arguments[i]).getRawType() != parameters[i] : arguments[i] != parameters[i])
return false;
}
}
return true;
}
for (int i = 0; i < parameters.length; i++) {
if (arguments[i] instanceof ParameterizedType ? ((ParameterizedType) arguments[i]).getRawType() != parameters[i] : arguments[i] != parameters[i]) {
return false;
}
}
}
return true;
}
@AllArgsConstructor
public static class Method {
private final java.lang.reflect.Method m;
@AllArgsConstructor
public static class Method {
private final java.lang.reflect.Method m;
public Object invoke(Object target, Object... arguments) {
try {
return m.invoke(target, arguments);
} catch (Exception e) {
throw new IllegalArgumentException("Cannot invoke method " + m, e);
}
}
}
public Object invoke(Object target, Object... arguments) {
try {
return m.invoke(target, arguments);
} catch (Exception e) {
throw new IllegalArgumentException("Cannot invoke method " + m, e);
}
}
}
public static Method getMethod(String className, String methodName, Class<?>... params) {
return getTypedMethod(getClass(className), methodName, null, params);
}
public static Method getTypedMethod(Class<?> clazz, String methodName, Class<?> returnType, Class<?>... params) {
for (final java.lang.reflect.Method method : clazz.getDeclaredMethods()) {
if ((methodName == null || method.getName().equals(methodName))
&& (returnType == null || method.getReturnType().equals(returnType))
&& Arrays.equals(method.getParameterTypes(), params)) {
method.setAccessible(true);
return new Method(method);
}
}
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... params) {
return getTypedMethod(clazz, methodName, null, params);
}
// Search in every superclass
if (clazz.getSuperclass() != null) {
return getTypedMethod(clazz.getSuperclass(), methodName, returnType, params);
}
public static Method getTypedMethod(Class<?> clazz, String methodName, Class<?> returnType, Class<?>... params) {
for (final java.lang.reflect.Method method : clazz.getDeclaredMethods()) {
if ((methodName == null || method.getName().equals(methodName))
&& (returnType == null || method.getReturnType().equals(returnType))
&& Arrays.equals(method.getParameterTypes(), params)) {
method.setAccessible(true);
return new Method(method);
}
}
throw new IllegalArgumentException(String.format("Cannot find method %s (%s).", methodName, Arrays.asList(params)));
}
// Search in every superclass
if (clazz.getSuperclass() != null)
return getTypedMethod(clazz.getSuperclass(), methodName, returnType, params);
@AllArgsConstructor
public static class Constructor {
private final java.lang.reflect.Constructor<?> c;
throw new IllegalArgumentException(String.format("Cannot find method %s (%s).", methodName, Arrays.asList(params)));
}
public Object invoke(Object... arguments) {
try {
return c.newInstance(arguments);
} catch (Exception e) {
throw new IllegalArgumentException("Cannot invoke constructor " + c, e);
}
}
}
@AllArgsConstructor
public static class Constructor {
private final java.lang.reflect.Constructor<?> c;
public static Constructor getConstructor(Class<?> clazz, Class<?>... params) {
for (final java.lang.reflect.Constructor<?> constructor : clazz.getDeclaredConstructors()) {
if (Arrays.equals(constructor.getParameterTypes(), params)) {
constructor.setAccessible(true);
return new Constructor(constructor);
}
}
public Object invoke(Object... arguments) {
try {
return c.newInstance(arguments);
} catch (Exception e) {
throw new IllegalArgumentException("Cannot invoke constructor " + c, e);
}
}
}
throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params)));
}
public static Constructor getConstructor(String className, Class<?>... params) {
return getConstructor(getClass(className), params);
}
public static Constructor getConstructor(Class<?> clazz, Class<?>... params) {
for (final java.lang.reflect.Constructor<?> constructor : clazz.getDeclaredConstructors()) {
if (Arrays.equals(constructor.getParameterTypes(), params)) {
constructor.setAccessible(true);
return new Constructor(constructor);
}
}
throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params)));
}
public static Object newInstance(Class<?> clazz) {
try {
if (Core.getVersion() > 15) {
return Unsafe.getUnsafe().allocateInstance(clazz);
} else {
return clazz.newInstance();
}
} catch (InstantiationException | IllegalAccessException e) {
throw new SecurityException("Could not create object", e);
}
}
public static Object newInstance(Class<?> clazz) {
try {
return Unsafe.getUnsafe().allocateInstance(clazz);
} catch (InstantiationException e) {
throw new SecurityException("Could not create object", e);
}
}
}
@@ -139,7 +139,8 @@ public class SWCommand extends AbstractSWCommand<CommandSender> {
}
if (args.length == 0 || atomicInteger.get() == commandList.size()) {
commandList.forEach(subCommand -> {
if (subCommand.validator == null || subCommand.validator.validate(p, p, (s, args1) -> {})) {
if (subCommand.validator == null || subCommand.validator.validate(p, p, (s, args1) -> {
})) {
send(p, subCommand);
}
});
@@ -21,8 +21,6 @@ package de.steamwar.command;
import org.bukkit.command.CommandSender;
import java.util.List;
public interface TypeMapper<T> extends AbstractTypeMapper<CommandSender, T> {
/**
* The CommandSender can be null!
@@ -19,28 +19,78 @@
package de.steamwar.core;
import de.steamwar.Reflection;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.BaseComponent;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializer;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import java.util.UUID;
public class BountifulWrapper {
private BountifulWrapper() {}
public static final BountifulWrapper impl = new BountifulWrapper();
public static final IBountifulWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public void playPling(Player player) {
player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1);
}
public interface IBountifulWrapper {
void playPling(Player player);
public void sendMessage(Player player, ChatMessageType type, BaseComponent... msg) {
if (type == ChatMessageType.CHAT) type = ChatMessageType.SYSTEM;
player.spigot().sendMessage(type, msg);
}
void sendMessage(Player player, ChatMessageType type, BaseComponent... msg);
private static final Class<?> dataWatcherRegistry = EntityDataSerializers.class;
private static final Class<?> dataWatcherSerializer = EntityDataSerializer.class;
Object getDataWatcherObject(int index, Class<?> type);
Object getDataWatcherItem(Object dataWatcherObject, Object value);
public Object getDataWatcherObject(int index, Class<?> type) {
return new EntityDataAccessor<>(index, (EntityDataSerializer<Object>) Reflection.getField(dataWatcherRegistry, dataWatcherSerializer, 0, type).get(null));
}
PositionSetter getPositionSetter(Class<?> packetClass, int fieldOffset);
PositionSetter getRelMoveSetter(Class<?> packetClass);
UUIDSetter getUUIDSetter(Class<?> packetClass);
public Object getDataWatcherItem(Object dwo, Object value) {
return new SynchedEntityData.DataItem<>((EntityDataAccessor<Object>) dwo, value);
}
public BountifulWrapper.PositionSetter getPositionSetter(Class<?> packetClass, int fieldOffset) {
try {
Reflection.Field<PositionMoveRotation> field = Reflection.getField(packetClass, PositionMoveRotation.class, 0);
return (packet, x, y, z, pitch, yaw) -> {
field.set(packet, new PositionMoveRotation(new Vec3(x, y, z), field.get(packet).deltaMovement(), yaw, pitch));
};
} catch (IllegalArgumentException e) {
Reflection.Field<Double> posX = Reflection.getField(packetClass, double.class, fieldOffset);
Reflection.Field<Double> posY = Reflection.getField(packetClass, double.class, fieldOffset + 1);
Reflection.Field<Double> posZ = Reflection.getField(packetClass, double.class, fieldOffset + 2);
boolean isByteClass = packetClass.getSimpleName().contains("PacketPlayOutEntityTeleport") || packetClass.getSimpleName().contains("PacketPlayOutNamedEntitySpawn");
Class<?> pitchYawType = isByteClass ? byte.class : int.class;
Reflection.Field<?> lookYaw = Reflection.getField(packetClass, pitchYawType, isByteClass ? 0 : 1);
Reflection.Field<?> lookPitch = Reflection.getField(packetClass, pitchYawType, isByteClass ? 1 : 2);
return (packet, x, y, z, pitch, yaw) -> {
posX.set(packet, x);
posY.set(packet, y);
posZ.set(packet, z);
if (isByteClass) {
lookYaw.set(packet, (byte) (yaw * 256 / 360));
lookPitch.set(packet, (byte) (pitch * 256 / 360));
} else {
lookYaw.set(packet, (int) (yaw * 256 / 360));
lookPitch.set(packet, (int) (pitch * 256 / 360));
}
};
}
}
public BountifulWrapper.UUIDSetter getUUIDSetter(Class<?> packetClass) {
Reflection.Field<UUID> uuidField = Reflection.getField(packetClass, UUID.class, 0);
return uuidField::set;
}
public interface PositionSetter {
@@ -1,29 +0,0 @@
/*
* 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.core;
public interface ChatWrapper {
ChatWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
Object stringToChatComponent(String text);
Object getDataWatcherPacket(int entityId, Object... dataWatcherKeyValues);
}
@@ -20,7 +20,8 @@
package de.steamwar.core;
public class CheckpointUtils {
private CheckpointUtils() {}
private CheckpointUtils() {
}
public static void signalHandler() {
try {
@@ -23,7 +23,10 @@ import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.sql.internal.Statement;
import io.netty.channel.ChannelFuture;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerConnectionListener;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftWorld;
import org.eclipse.openj9.criu.CRIUSupport;
import org.eclipse.openj9.criu.JVMCRIUException;
import sun.misc.Signal;
@@ -41,7 +44,8 @@ import java.util.logging.Level;
import java.util.stream.Stream;
class CheckpointUtilsJ9 {
private CheckpointUtilsJ9() {}
private CheckpointUtilsJ9() {
}
static void signalHandler() {
Signal.handle(new Signal("USR1"), signal -> Bukkit.getScheduler().runTask(Core.getInstance(), CheckpointUtils::freeze));
@@ -49,15 +53,15 @@ class CheckpointUtilsJ9 {
static void freeze() {
String checkpointFile = System.getProperty("checkpoint");
if(!CRIUSupport.isCheckpointAllowed() || checkpointFile == null) {
if (!CRIUSupport.isCheckpointAllowed() || checkpointFile == null) {
Bukkit.shutdown();
return;
}
Bukkit.getOnlinePlayers().forEach(player -> player.kickPlayer(null));
List<?> networkManagers = TinyProtocol.networkManagers.get(TinyProtocol.getServerConnection(Core.getInstance()));
if(!Bukkit.getOnlinePlayers().isEmpty() || !networkManagers.isEmpty()) {
List<?> networkManagers = TinyProtocol.instance.networkManagers;
if (!Bukkit.getOnlinePlayers().isEmpty() || !networkManagers.isEmpty()) {
Core.getInstance().getLogger().log(Level.INFO, "Waiting for players to disconnect for checkpointing");
Bukkit.getScheduler().runTaskLater(Core.getInstance(), CheckpointUtils::freeze, 1);
return;
@@ -69,7 +73,7 @@ class CheckpointUtilsJ9 {
freezeInternal(path);
} catch (Exception e) {
String message = e.getMessage() != null ? e.getMessage() : "";
if(message.contains("Connected TCP socket")) {
if (message.contains("Connected TCP socket")) {
Core.getInstance().getLogger().log(Level.INFO, "Connected TCP socket, waiting for checkpointing");
Bukkit.getScheduler().runTaskLater(Core.getInstance(), CheckpointUtils::freeze, 1);
return;
@@ -77,7 +81,7 @@ class CheckpointUtilsJ9 {
Bukkit.shutdown();
if(!message.contains("Can't dump ghost file") && !message.contains("Can't create link remap")) // File/Jar has been updated
if (!message.contains("Can't dump ghost file") && !message.contains("Can't create link remap")) // File/Jar has been updated
throw new SecurityException(e);
} finally {
// Delete checkpoint
@@ -90,18 +94,20 @@ class CheckpointUtilsJ9 {
}
private static final Reflection.Field<List> channelFutures = Reflection.getField(TinyProtocol.serverConnection, List.class, 0, ChannelFuture.class);
private static final Reflection.Method bind = Reflection.getMethod(TinyProtocol.serverConnection, null, InetAddress.class, int.class);
private static final Reflection.Field<List> channelFutures = Reflection.getField(ServerConnectionListener.class, List.class, 0, ChannelFuture.class);
private static void freezeInternal(Path path) throws Exception {
Bukkit.getPluginManager().callEvent(new CRIUSleepEvent());
Bukkit.getWorlds().forEach(FlatteningWrapper.impl::syncSave);
Bukkit.getWorlds().forEach(world -> {
((CraftWorld) world).getHandle().save(null, true, false);
});
Statement.closeAll();
// Close socket
Object serverConnection = TinyProtocol.getServerConnection(Core.getInstance());
ServerConnectionListener serverConnection = MinecraftServer.getServer().getConnection();
List<?> channels = channelFutures.get(serverConnection);
for(Object future : channels) {
for (Object future : channels) {
((ChannelFuture) future).channel().close().syncUninterruptibly();
}
channels.clear();
@@ -121,7 +127,7 @@ class CheckpointUtilsJ9 {
criu.checkpointJVM();
} catch (JVMCRIUException e) {
Path logfile = path.resolve("criu.log");
if(logfile.toFile().exists()) {
if (logfile.toFile().exists()) {
throw new IllegalStateException("Could not create checkpoint, criu log:\n" + new String(Files.readAllBytes(logfile)), e);
}
@@ -135,11 +141,9 @@ class CheckpointUtilsJ9 {
}
// Reopen socket
bind.invoke(serverConnection, InetAddress.getLoopbackAddress(), port);
if(Core.getVersion() > 12) {
for(Object future : channels) {
((ChannelFuture) future).channel().config().setAutoRead(true);
}
serverConnection.startTcpServerListener(InetAddress.getLoopbackAddress(), port);
for (Object future : channels) {
((ChannelFuture) future).channel().config().setAutoRead(true);
}
Bukkit.getPluginManager().callEvent(new CRIUWakeupEvent());
@@ -1,40 +0,0 @@
/*
* 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.core;
import de.steamwar.Reflection;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.SimpleCommandMap;
import java.util.Map;
@UtilityClass
public class CommandRemover {
private static final Reflection.Field<SimpleCommandMap> commandMap = Reflection.getField("org.bukkit.craftbukkit.CraftServer", "commandMap", SimpleCommandMap.class);
private static final Reflection.Field<Map> knownCommands = Reflection.getField(SimpleCommandMap.class, "knownCommands", Map.class);
public static void removeAll(String... cmds) {
Map<String, Command> knownCmds = knownCommands.get(commandMap.get(Bukkit.getServer()));
for (String cmd : cmds) {
knownCmds.remove(cmd.toLowerCase());
}
}
}
@@ -21,16 +21,11 @@ package de.steamwar.core;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.command.*;
import de.steamwar.core.authlib.SteamwarGameProfileRepository;
import de.steamwar.core.events.AntiNocom;
import de.steamwar.core.events.ChattingEvent;
import de.steamwar.core.events.PlayerJoinedEvent;
import de.steamwar.core.events.WorldLoadEvent;
import de.steamwar.command.SWCommandUtils;
import de.steamwar.command.SWTypeMapperCreator;
import de.steamwar.command.TabCompletionCache;
import de.steamwar.command.TypeMapper;
import de.steamwar.core.authlib.SteamwarGameProfileRepository;
import de.steamwar.linkage.AbstractLinker;
import de.steamwar.linkage.SpigotLinker;
import de.steamwar.message.Message;
@@ -52,52 +47,53 @@ import java.util.logging.Level;
public class Core extends JavaPlugin {
public static final Message MESSAGE = new Message("SpigotCore", Core.class.getClassLoader());
public static final Message MESSAGE = new Message("SpigotCore", Core.class.getClassLoader());
public static int getVersion(){
return Reflection.MAJOR_VERSION;
}
@Deprecated
public static int getVersion() {
return Reflection.MAJOR_VERSION;
}
@Getter
@Setter
private static JavaPlugin instance;
@Getter
@Setter
private static JavaPlugin instance;
@Getter
private static String serverName = "";
@Getter
private static String serverName = "";
public static void setServerName(String serverName) {
if (Core.serverName.isEmpty()) {
Core.serverName = serverName;
}
}
public static void setServerName(String serverName) {
if (Core.serverName.isEmpty()) {
Core.serverName = serverName;
}
}
private ErrorHandler errorHandler;
private CrashDetector crashDetector;
private ErrorHandler errorHandler;
private CrashDetector crashDetector;
@Override
public void onLoad() {
setInstance(this);
serverName = System.getProperty("serverName", "");
}
@Override
public void onEnable() {
new PlayerVersion();
@Override
public void onLoad() {
setInstance(this);
serverName = System.getProperty("serverName", "");
}
errorHandler = new ErrorHandler();
crashDetector = new CrashDetector();
@Override
public void onEnable() {
new PlayerVersion();
SWCommandUtils.init((SWTypeMapperCreator<TypeMapper<Object>, CommandSender, Object>) (mapper, tabCompleter) -> new TypeMapper<Object>() {
@Override
public Object map(CommandSender commandSender, String[] previousArguments, String s) {
return mapper.apply(s);
}
errorHandler = new ErrorHandler();
crashDetector = new CrashDetector();
@Override
public Collection<String> tabCompletes(CommandSender sender, String[] previousArguments, String s) {
return tabCompleter.apply(sender, s);
}
});
SWCommandUtils.init((SWTypeMapperCreator<TypeMapper<Object>, CommandSender, Object>) (mapper, tabCompleter) -> new TypeMapper<Object>() {
@Override
public Object map(CommandSender commandSender, String[] previousArguments, String s) {
return mapper.apply(s);
}
@Override
public Collection<String> tabCompletes(CommandSender sender, String[] previousArguments, String s) {
return tabCompleter.apply(sender, s);
}
});
SpigotLinker spigotLinker = new SpigotLinker(this, MESSAGE);
try {
@@ -108,36 +104,33 @@ public class Core extends JavaPlugin {
return;
}
Bukkit.getServer().getPluginManager().registerEvents(RecipeDiscoverWrapper.impl, this);
if(!Statement.productionDatabase()) {
getServer().getPluginManager().registerEvents(LocaleChangeWrapper.impl, this);
}
if (!Statement.productionDatabase()) {
getServer().getPluginManager().registerEvents(new LocaleChangeWrapper(), this);
}
getServer().getMessenger().registerIncomingPluginChannel(this, "sw:bridge", new NetworkReceiver());
getServer().getMessenger().registerOutgoingPluginChannel(this, "sw:bridge");
getServer().getMessenger().registerIncomingPluginChannel(this, "sw:bridge", new NetworkReceiver());
getServer().getMessenger().registerOutgoingPluginChannel(this, "sw:bridge");
if (Core.getVersion() != 20)
SteamwarGameProfileRepository.impl.inject();
SteamwarGameProfileRepository.impl.inject();
TinyProtocol.init();
CheckpointUtils.signalHandler();
TinyProtocol.init();
CheckpointUtils.signalHandler();
Bukkit.getScheduler().runTaskTimer(this, TabCompletionCache::invalidateOldEntries, 20, 20);
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), SteamwarUser::clear, 72000, 72000);
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), SchematicNode::clear, 20L * 30, 20L * 30);
Bukkit.getScheduler().runTaskTimer(this, TabCompletionCache::invalidateOldEntries, 20, 20);
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), SteamwarUser::clear, 72000, 72000);
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), SchematicNode::clear, 20L * 30, 20L * 30);
try {
getLogger().log(Level.INFO, "Running on: " + new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream())).readLine());
} catch (IOException e) {
throw new SecurityException("Could not load Hostname", e);
}
}
try {
getLogger().log(Level.INFO, "Running on: " + new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream())).readLine());
} catch (IOException e) {
throw new SecurityException("Could not load Hostname", e);
}
}
@Override
public void onDisable() {
TinyProtocol.instance.close();
errorHandler.unregister();
if(crashDetector.onMainThread())
Statement.closeAll();
}
@Override
public void onDisable() {
TinyProtocol.instance.close();
errorHandler.unregister();
if (crashDetector.onMainThread()) Statement.closeAll();
}
}
@@ -19,14 +19,18 @@
package de.steamwar.core;
import com.comphenix.tinyprotocol.TinyProtocol;
import lombok.experimental.UtilityClass;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import org.bukkit.craftbukkit.CraftChunk;
import org.bukkit.entity.Player;
@UtilityClass
public class CraftbukkitWrapper {
private CraftbukkitWrapper() {}
public static final ICraftbukkitWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public interface ICraftbukkitWrapper {
void sendChunk(Player p, int chunkX, int chunkZ);
public void sendChunk(Player p, int chunkX, int chunkZ) {
LevelChunk chunk = (LevelChunk) ((CraftChunk) p.getWorld().getChunkAt(chunkX, chunkZ)).getHandle(ChunkStatus.FULL);
TinyProtocol.instance.sendPacket(p, new ClientboundLevelChunkWithLightPacket(chunk, chunk.level.getLightEngine(), null, null, true));
}
}
@@ -37,7 +37,7 @@ public class CrashDetector {
private boolean run = true;
public CrashDetector () {
public CrashDetector() {
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
lastTick.set(System.nanoTime());
lastMessage.set(System.nanoTime());
@@ -60,11 +60,11 @@ public class CrashDetector {
SWException.init();
while (run) {
long curTime = System.nanoTime();
if(curTime - 4*TIMEOUT >= lastTick.get()) {
if (curTime - 4 * TIMEOUT >= lastTick.get()) {
SWException.log("Server did not recover in " + ((curTime - lastTick.get()) / 1000000.0) + "ms, unclean server stop", "");
hardStop();
} else if(curTime - TIMEOUT > lastMessage.get()) {
if(mainThread.isAlive()) {
} else if (curTime - TIMEOUT > lastMessage.get()) {
if (mainThread.isAlive()) {
SWException.log("Server hung for " + ((curTime - lastTick.get()) / 1000000.0) + "ms", Arrays.stream(mainThread.getStackTrace()).map(StackTraceElement::toString).collect(Collectors.joining("\n")));
} else {
SWException.log("Server thread already dead, unclean server stop", "Core enabled: " + Core.getInstance().isEnabled());
@@ -81,7 +81,7 @@ public class CrashDetector {
}
private void hardStop() {
if(Core.getInstance().isEnabled()) {
if (Core.getInstance().isEnabled()) {
Core.getInstance().onDisable();
}
Statement.closeAll();
@@ -21,6 +21,7 @@ package de.steamwar.core;
import de.steamwar.Reflection;
import de.steamwar.sql.SWException;
import org.spigotmc.WatchdogThread;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
@@ -36,13 +37,11 @@ public class ErrorHandler extends Handler {
private final long watchdogThreadId;
public ErrorHandler(){
public ErrorHandler() {
Logger.getLogger("").addHandler(this);
Class<?> watchdogThread = Reflection.getClass("org.spigotmc.WatchdogThread");
Reflection.Field<?> getInstance = Reflection.getField(watchdogThread, watchdogThread, 0);
Thread watchdog = (Thread) getInstance.get(null);
watchdogThreadId = watchdog.getId();
Reflection.Field<WatchdogThread> getInstance = Reflection.getField(WatchdogThread.class, WatchdogThread.class, 0);
watchdogThreadId = getInstance.get(null).getId();
}
void unregister() {
@@ -52,29 +51,30 @@ public class ErrorHandler extends Handler {
@Override
@SuppressWarnings("deprecation")
public void publish(LogRecord logRecord) {
if(logRecord.getLevel().intValue() < Level.WARNING.intValue())
return;
if (logRecord.getLevel().intValue() < Level.WARNING.intValue()) return;
if(watchdogThreadId == logRecord.getThreadID())
return;
if (watchdogThreadId == logRecord.getThreadID()) return;
String message = logRecord.getMessage() != null ? logRecord.getMessage() : "";
for(String reason : ignoreStartsWith)
if(message.startsWith(reason))
return;
for(String reason : ignoreContains)
if(message.contains(reason))
return;
for (String reason : ignoreStartsWith) {
if (message.startsWith(reason)) return;
}
for (String reason : ignoreContains) {
if (message.contains(reason)) return;
}
ByteArrayOutputStream stacktraceOutput = new ByteArrayOutputStream();
if(logRecord.getThrown() != null)
if (logRecord.getThrown() != null) {
logRecord.getThrown().printStackTrace(new PrintStream(stacktraceOutput));
}
String stacktrace = stacktraceOutput.toString();
if(stacktrace.contains("POI data mismatch") || stacktrace.contains("Newer version! Server downgrades are not supported!"))
if (stacktrace.contains("POI data mismatch") || stacktrace.contains("Newer version! Server downgrades are not supported!")) {
return;
}
if(message.isEmpty() && stacktrace.isEmpty())
if (message.isEmpty() && stacktrace.isEmpty()) {
return;
}
try {
SWException.log(message, stacktrace);
@@ -19,45 +19,16 @@
package de.steamwar.core;
import de.steamwar.Reflection;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import lombok.experimental.UtilityClass;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.contents.PlainTextContents;
import java.util.Optional;
@UtilityClass
public class FlatteningWrapper {
private FlatteningWrapper() {}
public static final Class<?> scoreboardObjective = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSetObjectivePacket");
public static final Class<?> scoreboardScore = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSetScorePacket");
public static final IFlatteningWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public interface IFlatteningWrapper {
void setScoreboardTitle(Object packet, String title);
void setScoreAction(Object packet);
Material getMaterial(String material);
Material getDye(int colorCode);
ItemStack setSkullOwner(String player);
Object getPose(EntityPose pose);
void setNamedSpawnPacketDataWatcher(Object packet);
Object formatDisplayName(String displayName);
void setSpawnPacketType(Object packet, EntityType type);
int getViewDistance(Player player);
void syncSave(World world);
}
public enum EntityPose {
NORMAL,
SNEAKING,
SWIMMING,
SHOOTING,
;
public Object formatDisplayName(String displayName) {
return displayName != null ? Optional.of((Object) MutableComponent.create(PlainTextContents.create(displayName))) : Optional.empty();
}
}
@@ -19,8 +19,17 @@
package de.steamwar.core;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLocaleChangeEvent;
public interface LocaleChangeWrapper extends Listener {
LocaleChangeWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
import java.util.Locale;
public class LocaleChangeWrapper implements Listener {
@EventHandler
private void onLocale(PlayerLocaleChangeEvent event) {
SteamwarUser.get(event.getPlayer().getUniqueId()).setLocale(Locale.forLanguageTag(event.getLocale()), false);
}
}
@@ -19,37 +19,30 @@
package de.steamwar.core;
import de.steamwar.Reflection;
import com.mojang.authlib.GameProfile;
import lombok.experimental.UtilityClass;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.world.level.GameType;
import org.bukkit.GameMode;
import java.util.function.LongSupplier;
import java.util.Collections;
import java.util.EnumSet;
public interface ProtocolWrapper {
@UtilityClass
public class ProtocolWrapper {
Class<?> itemStack = Reflection.getClass("net.minecraft.world.item.ItemStack");
Class<?> spawnPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundAddEntityPacket");
Class<?> spawnLivingPacket = Core.getVersion() > 18 ? ProtocolWrapper.spawnPacket : Reflection.getClass("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving");
Class<?> equipmentPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket");
public Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode) {
if (action == PlayerInfoAction.REMOVE) {
return new ClientboundPlayerInfoRemovePacket(Collections.singletonList(profile.getId()));
}
Class<?> enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "net.minecraft.world.level.GameType" : "net.minecraft.WorldSettings$EnumGamemode");
Reflection.Method getGameModeById = Reflection.getTypedMethod(enumGamemode, null, enumGamemode, int.class);
// 0: hand, 1: offhand, 2: feet, 3: legs, 4: chest, 5: head
Object[] itemSlots = Core.getVersion() > 8 ? Reflection.getClass("net.minecraft.world.entity.EnumItemSlot").getEnumConstants() : new Integer[]{0, 0, 1, 2, 3, 4};
ProtocolWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
void setEquipmentPacketStack(Object packet, Object slot, Object stack);
Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode);
default void initTPSWarp(LongSupplier longSupplier) {
Class<?> systemUtils = Reflection.getClass("net.minecraft.Util");
Reflection.getField(systemUtils, LongSupplier.class, 0).set(systemUtils, (LongSupplier) () -> System.nanoTime() + longSupplier.getAsLong());
return new ClientboundPlayerInfoUpdatePacket(action == PlayerInfoAction.ADD ?
EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE) : EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE),
Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.Entry(profile.getId(), profile, false, 0, GameType.byId(mode.getValue()), null, false, 0, null)));
}
enum PlayerInfoAction {
public enum PlayerInfoAction {
ADD,
GAMEMODE,
REMOVE
@@ -19,8 +19,15 @@
package de.steamwar.core;
import de.steamwar.linkage.Linked;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerRecipeDiscoverEvent;
public interface RecipeDiscoverWrapper extends Listener {
RecipeDiscoverWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
@Linked
public class RecipeDiscoverWrapper implements Listener {
@EventHandler
public void onRecipeDiscover(PlayerRecipeDiscoverEvent e) {
e.setCancelled(true);
}
}
@@ -25,7 +25,6 @@ import lombok.NonNull;
import lombok.experimental.Delegate;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.ClickEvent;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -145,7 +144,7 @@ public class SWPlayer {
return this;
}
private ThreadLocal<Message> messageThreadLocal = ThreadLocal.withInitial(() -> null);
private ThreadLocal<Message> messageThreadLocal = ThreadLocal.withInitial(() -> null);
public SWPlayer using(Message message) {
this.messageThreadLocal.set(message);
@@ -154,7 +153,9 @@ public class SWPlayer {
private Message getMessage() {
Message message = this.messageThreadLocal.get();
if (message == null) throw new IllegalStateException("Use #using(Message) before sending or parsing a message!");
if (message == null) {
throw new IllegalStateException("Use #using(Message) before sending or parsing a message!");
}
return message;
}
@@ -1,52 +0,0 @@
/*
* 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.core;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;
@UtilityClass
public class TPSWarpUtils {
private static long nanoOffset = 0;
private static long nanoDOffset = 0;
private static BukkitTask bukkitTask = null;
static {
ProtocolWrapper.impl.initTPSWarp(() -> nanoOffset);
}
public static void warp(double tps) {
double d = 50 - (50 / (tps / 20.0));
nanoDOffset = Math.max(0, (long) (d * 1000000));
if (nanoDOffset == 0) {
if (bukkitTask == null) return;
bukkitTask.cancel();
bukkitTask = null;
} else if (bukkitTask == null) {
bukkitTask = Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> nanoOffset += nanoDOffset, 1, 1);
}
}
public static boolean isWarping() {
return nanoDOffset > 0;
}
}
@@ -19,7 +19,7 @@
package de.steamwar.core;
import de.steamwar.Reflection;
import net.minecraft.server.MinecraftServer;
import org.bukkit.Bukkit;
public class TPSWatcher {
@@ -36,8 +36,9 @@ public class TPSWatcher {
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
long currentTime = System.currentTimeMillis();
if (currentTime > lastTime)
if (currentTime > lastTime) {
tps = (timeInterval / (double) (currentTime - lastTime)) * TICK_DEFAULT;
}
lastTime = currentTime;
}, timeInterval / 50, timeInterval / 50);
@@ -68,24 +69,17 @@ public class TPSWatcher {
case TEN_SECONDS:
return round(tps_TenSecond.tps);
case ONE_MINUTE:
return round(getSpigotTPS()[0]);
return round(MinecraftServer.getServer().tps1.getAverage());
case FIVE_MINUTES:
return round(getSpigotTPS()[1]);
return round(MinecraftServer.getServer().tps5.getAverage());
case TEN_MINUTES:
return round(getSpigotTPS()[2]);
return round(MinecraftServer.getServer().tps15.getAverage());
case ONE_SECOND:
default:
return round(tps_OneSecond.tps);
}
}
private static final Class<?> minecraftServer = Reflection.getClass("net.minecraft.server.MinecraftServer");
private static final Reflection.Method getServer = Reflection.getTypedMethod(minecraftServer, "getServer", minecraftServer);
private static final Reflection.Field<double[]> recentTps = Reflection.getField(minecraftServer, "recentTps", double[].class);
private static double[] getSpigotTPS() {
return recentTps.get(getServer.invoke(null));
}
private static double round(double d) {
return Math.round(d * 10) / 10.0;
}
@@ -1,28 +0,0 @@
/*
* 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.core;
import org.bukkit.Particle;
public interface TrickyParticleWrapper {
public TrickyParticleWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
Particle getVillagerHappy();
}
@@ -1,37 +0,0 @@
/*
* 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.core;
import com.mojang.authlib.properties.Property;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.EntityType;
public interface TrickyTrialsWrapper {
TrickyTrialsWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
EntityType getTntEntityType();
Enchantment getUnbreakingEnchantment();
Material getTurtleScute();
String getValue(Property property);
}
@@ -1,54 +0,0 @@
/*
* 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.core;
import org.bukkit.plugin.Plugin;
import java.lang.reflect.InvocationTargetException;
public class VersionDependent {
private VersionDependent() {}
public static <T> T getVersionImpl(Plugin plugin) {
return getVersionImpl(plugin, (new Exception()).getStackTrace()[1].getClassName());
}
public static <T> T getVersionImpl(Plugin plugin, String className){
return getVersionImpl(plugin, Core.getVersion(), className);
}
public static <T> T getVersionImpl(Plugin plugin, int fromVersion){
return getVersionImpl(plugin, fromVersion, (new Exception()).getStackTrace()[1].getClassName());
}
public static <T> T getVersionImpl(Plugin plugin, int fromVersion, String className){
ClassLoader loader = plugin.getClass().getClassLoader();
for(int version = fromVersion; version >= 8; version--) {
try {
return ((Class<? extends T>) Class.forName(className + version, true, loader)).getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new SecurityException("Could not load version dependent class", e);
} catch (ClassNotFoundException e) {
// try next version
}
}
throw new SecurityException("Unable to find version dependent implementation for " + className);
}
}
@@ -40,12 +40,12 @@ import org.bukkit.event.player.*;
import org.bukkit.util.Vector;
@Linked
@PluginCheck("WorldEdit")
@PluginCheck("FastAsyncWorldEdit")
public class WorldEditRenderer implements Listener {
private static WorldEditRenderer INSTANCE;
private static final Material WAND = FlatteningWrapper.impl.getMaterial("WOOD_AXE");
private static final Material WAND = Material.WOODEN_AXE;
private final WorldEditPlugin we;
@@ -89,7 +89,7 @@ public class WorldEditRenderer implements Listener {
private void renderRegion(Player player, LocalSession session, boolean scheduled) {
World world = session.getSelectionWorld();
if(world != null) {
if (world != null) {
RegionSelector regionSelector = session.getRegionSelector(world);
try {
Region region = regionSelector.getRegion();
@@ -102,7 +102,7 @@ public class WorldEditRenderer implements Listener {
private void drawCuboid(Vector min, Vector max, boolean scheduled, boolean clipboard, Player owner) {
//noinspection deprecation
if(owner.getItemInHand().getType() != WAND) {
if (owner.getItemInHand().getType() != WAND) {
WorldEditRendererWrapper.impl.hide(owner, true, true);
WorldEditRendererWrapper.impl.hide(owner, false, true);
} else {
@@ -114,7 +114,7 @@ public class WorldEditRenderer implements Listener {
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if(event.getPlayer().getItemInHand().getType() == WAND) {
if (event.getPlayer().getItemInHand().getType() == WAND) {
WorldEditRendererWrapper.impl.tick(event.getPlayer());
}
renderClipboard(event.getPlayer(), we.getSession(event.getPlayer()), false);
@@ -104,10 +104,10 @@ public class WorldEditRendererCUIEditor implements Listener {
@AllArgsConstructor
public enum Width {
HUGE(15, "WORLDEDIT_CUI_WIDTH_HUGE", 2/16f),
LARGE(8, "WORLDEDIT_CUI_WIDTH_LARGE", 1/16f),
MEDIUM(4, "WORLDEDIT_CUI_WIDTH_MEDIUM", 1/32f),
SLIM(0, "WORLDEDIT_CUI_WIDTH_SLIM", 1/64f);
HUGE(15, "WORLDEDIT_CUI_WIDTH_HUGE", 2 / 16f),
LARGE(8, "WORLDEDIT_CUI_WIDTH_LARGE", 1 / 16f),
MEDIUM(4, "WORLDEDIT_CUI_WIDTH_MEDIUM", 1 / 32f),
SLIM(0, "WORLDEDIT_CUI_WIDTH_SLIM", 1 / 64f);
public final int lightLevel;
public final String name;
@@ -116,9 +116,7 @@ public class WorldEditRendererCUIEditor implements Listener {
public WorldEditRendererCUIEditor() {
Bukkit.getPluginManager().registerEvents(this, Core.getInstance());
if (Core.getVersion() >= 20) {
new Command();
}
new Command();
}
private static class Command extends SWCommand {
@@ -0,0 +1,79 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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.core;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public class WorldEditRendererFallback {
private static final int VIEW_DISTANCE = 64;
private static final int SQ_VIEW_DISTANCE = VIEW_DISTANCE * VIEW_DISTANCE;
private static final double STEP_SIZE = 0.5;
private static final Vector ONES = new Vector(1, 1, 1);
private static final Vector STEPS = new Vector(STEP_SIZE, STEP_SIZE, STEP_SIZE);
public void draw(Player player, boolean scheduled, boolean clipboard, Vector min, Vector max) {
if (!scheduled) return;
max = max.clone().add(ONES);
drawLine(player, clipboard, new Vector(min.getX(), min.getY(), min.getZ()), new Vector(max.getX(), min.getY(), min.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), max.getY(), min.getZ()), new Vector(max.getX(), max.getY(), min.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), min.getY(), max.getZ()), new Vector(max.getX(), min.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), max.getY(), max.getZ()), new Vector(max.getX(), max.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), min.getY(), min.getZ()), new Vector(min.getX(), max.getY(), min.getZ()));
drawLine(player, clipboard, new Vector(max.getX(), min.getY(), min.getZ()), new Vector(max.getX(), max.getY(), min.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), min.getY(), max.getZ()), new Vector(min.getX(), max.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(max.getX(), min.getY(), max.getZ()), new Vector(max.getX(), max.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), min.getY(), min.getZ()), new Vector(min.getX(), min.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(max.getX(), min.getY(), min.getZ()), new Vector(max.getX(), min.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), max.getY(), min.getZ()), new Vector(min.getX(), max.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(max.getX(), max.getY(), min.getZ()), new Vector(max.getX(), max.getY(), max.getZ()));
}
private void drawLine(Player player, boolean clipboard, Vector min, Vector max) {
Particle particle;
if (clipboard) {
particle = Particle.HAPPY_VILLAGER;
} else {
particle = Particle.DRAGON_BREATH;
}
Vector stepSize = max.clone().subtract(min).normalize().multiply(STEPS);
while (min.getX() <= max.getX() && min.getY() <= max.getY() && min.getZ() <= max.getZ()) {
Location location = player.getLocation();
double dx = min.getX() - location.getX();
double dy = min.getY() - location.getY();
double dz = min.getZ() - location.getZ();
if (dx * dx + dy * dy + dz * dz > SQ_VIEW_DISTANCE) {
min.add(stepSize);
continue;
}
player.spawnParticle(particle, min.getX(), min.getY(), min.getZ(), 1, 0.0, 0.0, 0.0, 0.0);
min.add(stepSize);
}
}
}
@@ -19,12 +19,19 @@
package de.steamwar.core;
import de.steamwar.entity.CWireframe;
import de.steamwar.entity.REntityServer;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public interface WorldEditRendererWrapper {
WorldEditRendererWrapper fallback = VersionDependent.getVersionImpl(Core.getInstance(), 9);
WorldEditRendererWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
import java.util.HashMap;
import java.util.Map;
public class WorldEditRendererWrapper {
public static final WorldEditRendererFallback fallback = new WorldEditRendererFallback();
public static final WorldEditRendererWrapper impl = new WorldEditRendererWrapper();
static void safeDraw(Player player, boolean scheduled, boolean clipboard, Vector pos1, Vector pos2) {
if (PlayerVersion.isBedrock(player) || PlayerVersion.getVersion(player) < 20) {
@@ -34,11 +41,83 @@ public interface WorldEditRendererWrapper {
}
}
void draw(Player player, boolean scheduled, boolean clipboard, Vector pos1, Vector pos2);
private static final class BoxPair {
private CWireframe regionBox;
private CWireframe clipboardBox;
void tick(Player player);
public CWireframe get(boolean clipboard) {
if (clipboard) {
return clipboardBox;
} else {
return regionBox;
}
}
void hide(Player player, boolean clipboard, boolean hide);
public void set(boolean clipboard, CWireframe box) {
if (clipboard) {
this.clipboardBox = box;
} else {
this.regionBox = box;
}
}
void remove(Player player);
public void die() {
if (clipboardBox != null) {
clipboardBox.die();
}
if (regionBox != null) {
regionBox.die();
}
}
}
private static final Map<Player, REntityServer> servers = new HashMap<>();
private static final Map<Player, BoxPair> boxes = new HashMap<>();
public void draw(Player player, boolean scheduled, boolean clipboard, Vector pos1, Vector pos2) {
REntityServer server = servers.computeIfAbsent(player, __ -> {
REntityServer entityServer = new REntityServer();
entityServer.addPlayer(player);
return entityServer;
});
WorldEditRendererCUIEditor.Type type = clipboard ? WorldEditRendererCUIEditor.Type.CLIPBOARD : WorldEditRendererCUIEditor.Type.SELECTION;
float width = type.getWidth(player).value;
Material material = type.getMaterial(player);
if (material == Material.BARRIER) {
hide(player, clipboard, true);
return;
}
BlockData block = material.createBlockData();
BoxPair boxPair = boxes.computeIfAbsent(player, __ -> new BoxPair());
CWireframe box = boxPair.get(clipboard);
if (box == null) {
box = new CWireframe(server);
boxPair.set(clipboard, box);
}
box.setPos1And2(pos1.toLocation(player.getWorld()), pos2.toLocation(player.getWorld()));
box.setWidth(width);
box.setBlock(block);
}
public void tick(Player player) {
REntityServer server = servers.get(player);
if (server != null) server.tick();
}
public void hide(Player player, boolean clipboard, boolean hide) {
BoxPair boxPair = boxes.get(player);
if (boxPair == null) return;
CWireframe box = boxPair.get(clipboard);
if (box == null) return;
box.hide(hide);
}
public void remove(Player player) {
BoxPair boxPair = boxes.remove(player);
if (boxPair != null) boxPair.die();
REntityServer server = servers.remove(player);
if (server != null) server.close();
}
}
@@ -19,9 +19,18 @@
package de.steamwar.core;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV2;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV3;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.EmptyClipboardException;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.*;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV1Reader;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV2Reader;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV3Reader;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder;
@@ -30,25 +39,140 @@ import de.steamwar.sql.NodeData;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import org.enginehub.linbus.stream.LinBinaryIO;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.logging.Level;
public interface WorldEditWrapper {
WorldEditWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public class WorldEditWrapper {
public static final WorldEditWrapper impl = new WorldEditWrapper();
InputStream getPlayerClipboard(Player player);
public InputStream getPlayerClipboard(Player player) {
return WorldEditWrapper.getPlayerClipboard(player, (outputStream, clipboard, clipboardHolder) -> {
ClipboardWriter writer = BuiltInClipboardFormat.FAST_V3.getWriter(outputStream);
writer.write(clipboard);
writer.close();
});
}
void setPlayerClipboard(Player player, Clipboard clipboard);
Clipboard getClipboard(NodeData data) throws IOException;
Clipboard getClipboard(InputStream inputStream) throws IOException;
public void setPlayerClipboard(Player player, Clipboard clipboard) {
Actor actor = WorldEditWrapper.getWorldEditPlugin().wrapCommandSender(player);
WorldEditWrapper.getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard));
}
Vector getOrigin(Clipboard clipboard);
Vector getMinimum(Region region);
Vector getMaximum(Region region);
Vector applyTransform(Vector vector, Transform transform);
public Clipboard getClipboard(NodeData data) throws IOException {
ResetableInputStream is = new ResetableInputStream(data.schemData(false));
for (ClipboardFormat clipboardFormat : ClipboardFormats.getAll()) {
FilterInputStream fis = new FilterInputStream(is) {
@Override
public void close() throws IOException {
// Ignore close call!
}
};
boolean canBeRead = clipboardFormat.isFormat(fis);
is.reset();
if (!canBeRead) continue;
return clipboardFormat.load(is);
}
throw new IOException("No clipboard found");
}
NodeData.SchematicFormat getNativeFormat();
private static final Function<InputStream, ClipboardReader> FastV3 = FastSchematicReaderV3::new;
@SuppressWarnings("removal")
private static final Function<InputStream, ClipboardReader> FastV2 = inputStream -> new FastSchematicReaderV2(new NBTInputStream(inputStream));
@SuppressWarnings("removal")
private static final Function<InputStream, ClipboardReader> McEdit = inputStream -> new MCEditSchematicReader(new NBTInputStream(inputStream));
private static final Function<InputStream, ClipboardReader> SpongeV3 = inputStream -> new SpongeSchematicV3Reader(LinBinaryIO.read(new DataInputStream(inputStream)));
private static final Function<InputStream, ClipboardReader> SpongeV2 = inputStream -> new SpongeSchematicV2Reader(LinBinaryIO.read(new DataInputStream(inputStream)));
private static final Function<InputStream, ClipboardReader> SpongeV1 = inputStream -> new SpongeSchematicV1Reader(LinBinaryIO.read(new DataInputStream(inputStream)));
private static final Function<InputStream, ClipboardReader>[] READERS = new Function[]{
FastV3,
FastV2,
SpongeV3,
SpongeV2,
SpongeV1,
McEdit
};
public Clipboard getClipboard(InputStream inputStream) throws IOException {
ResetableInputStream is = new ResetableInputStream(inputStream);
for (Function<InputStream, ClipboardReader> reader : READERS) {
FilterInputStream fis = new FilterInputStream(is) {
@Override
public void close() throws IOException {
// Ignore close call!
}
};
try {
return reader.apply(fis).read();
} catch (Exception e) {
is.reset();
}
}
is.close();
throw new IOException("No clipboard found");
}
private static class ResetableInputStream extends InputStream {
private InputStream inputStream;
private int pointer = 0;
private List<Integer> list = new ArrayList<>();
public ResetableInputStream(InputStream in) {
this.inputStream = in;
}
@Override
public int read() throws IOException {
if (pointer >= list.size()) {
int data = inputStream.read();
list.add(data);
pointer++;
return data;
}
int data = list.get(pointer);
pointer++;
return data;
}
@Override
public void reset() throws IOException {
pointer = 0;
}
@Override
public void close() throws IOException {
list.clear();
pointer = -1;
}
}
public org.bukkit.util.Vector getOrigin(Clipboard clipboard) {
return new org.bukkit.util.Vector(clipboard.getOrigin().x(), clipboard.getOrigin().y(), clipboard.getOrigin().z());
}
public Vector getMinimum(Region region) {
return new Vector(region.getMinimumPoint().x(), region.getMinimumPoint().y(), region.getMinimumPoint().z());
}
public Vector getMaximum(Region region) {
return new Vector(region.getMaximumPoint().x(), region.getMaximumPoint().y(), region.getMaximumPoint().z());
}
public Vector applyTransform(Vector vector, Transform transform) {
Vector3 v = Vector3.at(vector.getX(), vector.getY(), vector.getZ());
v = transform.apply(v);
return new org.bukkit.util.Vector(v.x(), v.y(), v.z());
}
public NodeData.SchematicFormat getNativeFormat() {
return NodeData.SchematicFormat.SPONGE_V3;
}
static WorldEditPlugin getWorldEditPlugin() {
return (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit");
@@ -63,8 +187,7 @@ public interface WorldEditWrapper {
}
Clipboard clipboard = clipboardHolder.getClipboard();
if(clipboard == null)
throw new NoClipboardException();
if (clipboard == null) throw new NoClipboardException();
PipedOutputStream outputStream = new PipedOutputStream();
PipedInputStream inputStream;
@@ -19,15 +19,57 @@
package de.steamwar.core;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.linkage.Linked;
import net.minecraft.network.protocol.game.ClientboundLoginPacket;
import net.minecraft.network.protocol.game.CommonPlayerSpawnInfo;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
@Linked
public class WorldIdentifier {
private static final IWorldIdentifier impl = VersionDependent.getVersionImpl(Core.getInstance());
private static ResourceKey<Level> resourceKey = null;
private static final Class<?> resourceKeyClass = ResourceKey.class;
private static final Class<?> minecraftKeyClass = ResourceLocation.class;
private static final Reflection.Constructor resourceKeyConstructor = Reflection.getConstructor(resourceKeyClass, minecraftKeyClass, minecraftKeyClass);
private static final Reflection.Constructor minecraftKeyConstructor = Reflection.getConstructor(minecraftKeyClass, String.class, String.class);
public static void set(String name) {
impl.setResourceKey(name);
resourceKey = (ResourceKey<Level>) resourceKeyConstructor.invoke(minecraftKeyConstructor.invoke("minecraft", "dimension"), minecraftKeyConstructor.invoke("steamwar", name));
}
protected interface IWorldIdentifier {
void setResourceKey(String name);
public WorldIdentifier() {
TinyProtocol.instance.addFilter(ClientboundLoginPacket.class, (player, o) -> {
if (resourceKey == null) return o;
ClientboundLoginPacket packet = (ClientboundLoginPacket) o;
return new ClientboundLoginPacket(packet.playerId(),
packet.hardcore(),
packet.levels(),
packet.maxPlayers(),
packet.chunkRadius(),
packet.simulationDistance(),
packet.reducedDebugInfo(),
packet.showDeathScreen(),
packet.doLimitedCrafting(),
new CommonPlayerSpawnInfo(
packet.commonPlayerSpawnInfo().dimensionType(),
resourceKey,
packet.commonPlayerSpawnInfo().seed(),
packet.commonPlayerSpawnInfo().gameType(),
packet.commonPlayerSpawnInfo().previousGameType(),
packet.commonPlayerSpawnInfo().isDebug(),
packet.commonPlayerSpawnInfo().isFlat(),
packet.commonPlayerSpawnInfo().lastDeathLocation(),
packet.commonPlayerSpawnInfo().portalCooldown(),
packet.commonPlayerSpawnInfo().seaLevel()
),
packet.enforcesSecureChat()
);
});
}
}
@@ -19,12 +19,56 @@
package de.steamwar.core.authlib;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.GameProfileRepository;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
import com.mojang.authlib.ProfileLookupCallback;
import de.steamwar.Reflection;
import de.steamwar.sql.SteamwarUser;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.Services;
public abstract class SteamwarGameProfileRepository implements GameProfileRepository {
public static final SteamwarGameProfileRepository impl = VersionDependent.getVersionImpl(Core.getInstance());
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public abstract void inject();
public class SteamwarGameProfileRepository implements GameProfileRepository {
public static final SteamwarGameProfileRepository impl = new SteamwarGameProfileRepository();
private static final GameProfileRepository fallback;
private static final Reflection.Field<Services> field;
private static final Services current;
static {
Class<?> clazz = MinecraftServer.getServer().getClass();
field = Reflection.getField(clazz, Services.class, 0);
current = field.get(MinecraftServer.getServer());
fallback = current.profileRepository();
}
@Override
public void findProfilesByNames(String[] strings, ProfileLookupCallback profileLookupCallback) {
List<String> unknownNames = new ArrayList<>();
for (String name : strings) {
SteamwarUser user = SteamwarUser.get(name);
if (user == null) {
unknownNames.add(name);
continue;
}
profileLookupCallback.onProfileLookupSucceeded(new GameProfile(user.getUUID(), user.getUserName()));
}
if (!unknownNames.isEmpty()) {
fallback.findProfilesByNames(unknownNames.toArray(new String[0]), profileLookupCallback);
}
}
@Override
public Optional<GameProfile> findProfileByName(String s) {
return fallback.findProfileByName(s);
}
public void inject() {
Services newServices = new Services(current.sessionService(), current.servicesKeySet(), this, current.profileCache(), current.paperConfigurations());
field.set(MinecraftServer.getServer(), newServices);
}
}
@@ -25,6 +25,10 @@ import de.steamwar.core.Core;
import de.steamwar.linkage.Linked;
import de.steamwar.sql.SWException;
import de.steamwar.techhider.ProtocolUtils;
import de.steamwar.techhider.TechHider;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket;
import net.minecraft.world.phys.BlockHitResult;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -42,9 +46,7 @@ public class AntiNocom implements Listener {
public AntiNocom() {
TinyProtocol.instance.addFilter(blockDig, this::onDig);
if(Core.getVersion() > 8) {
registerUseItem();
}
registerUseItem();
}
@EventHandler
@@ -53,18 +55,13 @@ public class AntiNocom implements Listener {
}
private void registerUseItem() {
Class<?> useItem = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundUseItemOnPacket");
Class<?> useItem = ServerboundUseItemOnPacket.class;
Function<Object, Object> getPosition;
if(Core.getVersion() > 12) {
Class<?> movingObjectPositionBlock = Reflection.getClass("net.minecraft.world.phys.BlockHitResult");
Reflection.Field<?> useItemPosition = Reflection.getField(useItem, movingObjectPositionBlock, 0);
Reflection.Field<?> movingBlockPosition = Reflection.getField(movingObjectPositionBlock, TechHider.blockPosition, 0);
Class<?> movingObjectPositionBlock = BlockHitResult.class;
Reflection.Field<?> useItemPosition = Reflection.getField(useItem, movingObjectPositionBlock, 0);
Reflection.Field<?> movingBlockPosition = Reflection.getField(movingObjectPositionBlock, TechHider.blockPosition, 0);
getPosition = (packet) -> movingBlockPosition.get(useItemPosition.get(packet));
} else {
getPosition = Reflection.getField(useItem, TechHider.blockPosition, 0)::get;
}
Function<Object, Object> getPosition = (packet) -> movingBlockPosition.get(useItemPosition.get(packet));
TinyProtocol.instance.addFilter(useItem, (player, packet) -> {
Object pos = getPosition.apply(packet);
@@ -72,21 +69,25 @@ public class AntiNocom implements Listener {
});
}
private static final Class<?> blockDig = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundPlayerActionPacket");
private static final Class<?> blockDig = ServerboundPlayerActionPacket.class;
private static final Reflection.Field<?> digPosition = Reflection.getField(blockDig, TechHider.blockPosition, 0);
private Object onDig(Player player, Object packet) {
Object pos = digPosition.get(packet);
return isValid(player, "Dig", TechHider.blockPositionX.get(pos), TechHider.blockPositionZ.get(pos)) ? packet : null;
}
private boolean isValid(Player player, String type, int x, int z) {
if((x == 0 && z == 0) || player.getWorld().isChunkLoaded(ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(z)))
if ((x == 0 && z == 0) || player.getWorld().isChunkLoaded(ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(z))) {
return true;
}
int amount = flags.compute(player, (p, a) -> a == null ? 1 : a+1);
if(amount % 8 == 0) { // Only after 8 and every 8 to reduce false flags and spam
if(amount == 8) // Schedule player kick only once
int amount = flags.compute(player, (p, a) -> a == null ? 1 : a + 1);
if (amount % 8 == 0) { // Only after 8 and every 8 to reduce false flags and spam
if (amount == 8) {
// Schedule player kick only once
Bukkit.getScheduler().runTask(Core.getInstance(), () -> player.kickPlayer(null));
}
SWException.log(player.getName() + " kicked for potential NoCom-DOS attack", x + " " + z + " " + type + " " + amount);
}
@@ -29,9 +29,9 @@ import org.bukkit.event.player.AsyncPlayerChatEvent;
@Linked
public class ChattingEvent implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
private void onChat(AsyncPlayerChatEvent event) {
event.setCancelled(true);
Core.MESSAGE.broadcastPrefixless("LOCAL_CHAT", event.getPlayer().getDisplayName(), event.getMessage());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
private void onChat(AsyncPlayerChatEvent event) {
event.setCancelled(true);
Core.MESSAGE.broadcastPrefixless("LOCAL_CHAT", event.getPlayer().getDisplayName(), event.getMessage());
}
}
@@ -34,28 +34,29 @@ import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
@Linked
public class PlayerJoinedEvent implements Listener{
public class PlayerJoinedEvent implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
private void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
SteamwarUser user = Statement.productionDatabase() ? SteamwarUser.get(player.getUniqueId()) : SteamwarUser.getOrCreate(player.getUniqueId(), player.getName(), uuid -> {});
@EventHandler(priority = EventPriority.LOWEST)
private void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
SteamwarUser user = Statement.productionDatabase() ? SteamwarUser.get(player.getUniqueId()) : SteamwarUser.getOrCreate(player.getUniqueId(), player.getName(), uuid -> {
});
UserPerm.Prefix prefix = user.prefix();
if(prefix != UserPerm.emptyPrefix) {
player.setDisplayName(prefix.getColorCode() + prefix.getChatPrefix() + " " + player.getName() + "§r");
} else
player.setDisplayName(prefix.getColorCode() + player.getName() + "§r");
UserPerm.Prefix prefix = user.prefix();
if (prefix != UserPerm.emptyPrefix) {
player.setDisplayName(prefix.getColorCode() + prefix.getChatPrefix() + " " + player.getName() + "§r");
} else
player.setDisplayName(prefix.getColorCode() + player.getName() + "§r");
event.setJoinMessage("§a§l» §r" + player.getDisplayName());
AuditLog.createJoin(Core.getServerName(), BauServerInfo.getOwnerUser(), user);
}
@EventHandler(priority = EventPriority.LOWEST)
private void onQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
event.setJoinMessage("§a§l» §r" + player.getDisplayName());
AuditLog.createJoin(Core.getServerName(), BauServerInfo.getOwnerUser(), user);
}
event.setQuitMessage("§c§l« §r" + player.getDisplayName());
AuditLog.createLeave(Core.getServerName(), BauServerInfo.getOwnerUser(), SteamwarUser.get(player.getUniqueId()));
}
@EventHandler(priority = EventPriority.LOWEST)
private void onQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
event.setQuitMessage("§c§l« §r" + player.getDisplayName());
AuditLog.createLeave(Core.getServerName(), BauServerInfo.getOwnerUser(), SteamwarUser.get(player.getUniqueId()));
}
}
@@ -28,8 +28,8 @@ import org.bukkit.event.world.WorldInitEvent;
public class WorldLoadEvent implements Listener {
@EventHandler
public void onWorldInit(WorldInitEvent e){
if(Integer.parseInt(System.getProperty("fightID", "0")) != -1) // On testarenas not
public void onWorldInit(WorldInitEvent e) {
if (Integer.parseInt(System.getProperty("fightID", "0")) != -1) // On testarenas not
e.getWorld().setKeepSpawnInMemory(false);
}
}
@@ -126,6 +126,7 @@ public class CLine extends CEntity {
}
private RBlockDisplay startLine;
private void updateStart() {
Vector vec = to.clone().subtract(from).toVector();
if (vec.length() > 35) vec.normalize().multiply(35);
@@ -145,6 +146,7 @@ public class CLine extends CEntity {
}
private RBlockDisplay middleLine;
private void updateMiddle() {
Vector vec = to.clone().subtract(from).toVector();
if (vec.length() <= 70) {
@@ -228,6 +230,7 @@ public class CLine extends CEntity {
}
private RBlockDisplay endLine;
private void updateEnd() {
Vector vec = to.clone().subtract(from).toVector();
if (vec.length() <= 35) {
@@ -1,30 +0,0 @@
/*
* 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.entity;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
public interface PacketConstructor {
PacketConstructor impl = VersionDependent.getVersionImpl(Core.getInstance());
Object teleportPacket(int entityId, double x, double y, double z, float yaw, float pitch);
Object createRPlayerSpawn(RPlayer player);
}
@@ -19,9 +19,9 @@
package de.steamwar.entity;
import de.steamwar.core.BountifulWrapper;
import de.steamwar.core.Core;
import lombok.Getter;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
@@ -29,26 +29,7 @@ import java.util.function.Consumer;
public class RArmorStand extends REntity {
private static int sizeIndex() {
switch(Core.getVersion()) {
case 8:
case 9:
return 10;
case 10:
case 12:
return 11;
case 14:
return 13;
case 15:
return 14;
case 18:
case 19:
default:
return 15;
}
}
private static final Object sizeWatcher = BountifulWrapper.impl.getDataWatcherObject(sizeIndex(), Byte.class);
private static final EntityDataAccessor<Byte> sizeWatcher = new EntityDataAccessor<>(15, EntityDataSerializers.BYTE);
@Getter
private final Size size;
@@ -63,8 +44,9 @@ public class RArmorStand extends REntity {
void spawn(Consumer<Object> packetSink) {
super.spawn(packetSink);
if(size != null && size != Size.NORMAL)
if (size != null && size != Size.NORMAL) {
packetSink.accept(getDataWatcherPacket(sizeWatcher, size.value));
}
}
public enum Size {
@@ -19,13 +19,14 @@
package de.steamwar.entity;
import de.steamwar.Reflection;
import de.steamwar.core.BountifulWrapper;
import de.steamwar.core.Core;
import lombok.Getter;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.entity.EntityType;
import java.util.function.BiConsumer;
@@ -58,12 +59,11 @@ public class RBlockDisplay extends RDisplay {
sendPacket(updatePacketSink, this::getBlock);
}
private static final Class<?> iBlockDataClass = Reflection.getClass("net.minecraft.world.level.block.state.BlockState");
private static final Reflection.Method getState = Reflection.getTypedMethod(Reflection.getClass("org.bukkit.craftbukkit.block.data.CraftBlockData"), "getState", iBlockDataClass);
private static final Object blockWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 23 : 22, iBlockDataClass);
private static final EntityDataAccessor<BlockState> blockWatcher = new EntityDataAccessor<>(23, EntityDataSerializers.BLOCK_STATE);
private void getBlock(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || !block.getAsString(true).equals(DEFAULT_BLOCK.getAsString(true))) {
packetSink.accept(blockWatcher, getState.invoke(block));
packetSink.accept(blockWatcher, ((CraftBlockData) block).getState());
}
}
}
@@ -19,10 +19,10 @@
package de.steamwar.entity;
import de.steamwar.core.BountifulWrapper;
import de.steamwar.core.Core;
import lombok.Getter;
import lombok.NonNull;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.entity.Display;
@@ -36,9 +36,6 @@ import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
* !! This class cannot be used in Versions lower than or equal to 1.19.4 !!
*/
@Getter
public abstract class RDisplay extends REntity {
@@ -111,10 +108,10 @@ public abstract class RDisplay extends REntity {
sendPacket(updatePacketSink, this::getTransformData);
}
private static final Object translationWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 11 : 10, Vector3f.class);
private static final Object leftRotationWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 13 : 12, Quaternionf.class);
private static final Object scaleWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 12 : 11, Vector3f.class);
private static final Object rightRotationWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 14 : 13, Quaternionf.class);
private static final EntityDataAccessor<Vector3f> translationWatcher = new EntityDataAccessor<>(11, EntityDataSerializers.VECTOR3);
private static final EntityDataAccessor<Quaternionf> leftRotationWatcher = new EntityDataAccessor<>(13, EntityDataSerializers.QUATERNION);
private static final EntityDataAccessor<Vector3f> scaleWatcher = new EntityDataAccessor<>(12, EntityDataSerializers.VECTOR3);
private static final EntityDataAccessor<Quaternionf> rightRotationWatcher = new EntityDataAccessor<>(14, EntityDataSerializers.QUATERNION);
private void getTransformData(boolean ignoreDefault, BiConsumer<Object, Object> dataSink) {
if (ignoreDefault || !transform.equals(DEFAULT_TRANSFORM)) {
@@ -130,8 +127,8 @@ public abstract class RDisplay extends REntity {
sendPacket(updatePacketSink, this::getInterpolationDuration);
}
private static final Object transformationInterpolationDurationWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 9 : 8, Integer.class);
private static final Object positionOrRotationInterpolationDurationWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 10 : 9, Integer.class);
private static final EntityDataAccessor<Integer> transformationInterpolationDurationWatcher = new EntityDataAccessor<>(9, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> positionOrRotationInterpolationDurationWatcher = new EntityDataAccessor<>(10, EntityDataSerializers.INT);
private void getInterpolationDuration(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || interpolationDelay != 0) {
@@ -145,7 +142,7 @@ public abstract class RDisplay extends REntity {
sendPacket(updatePacketSink, this::getViewRange);
}
private static final Object viewRangeWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 17 : 16, Float.class);
private static final EntityDataAccessor<Float> viewRangeWatcher = new EntityDataAccessor<>(17, EntityDataSerializers.FLOAT);
private void getViewRange(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || viewRange != 1.0F) {
@@ -158,7 +155,7 @@ public abstract class RDisplay extends REntity {
sendPacket(updatePacketSink, this::getShadowRadius);
}
private static final Object shadowRadiusWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 18 : 17, Float.class);
private static final EntityDataAccessor<Float> shadowRadiusWatcher = new EntityDataAccessor<>(18, EntityDataSerializers.FLOAT);
private void getShadowRadius(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || shadowRadius != 0.0F) {
@@ -171,7 +168,7 @@ public abstract class RDisplay extends REntity {
sendPacket(updatePacketSink, this::getShadowStrength);
}
private static final Object shadowStrengthWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 19 : 18, Float.class);
private static final EntityDataAccessor<Float> shadowStrengthWatcher = new EntityDataAccessor<>(19, EntityDataSerializers.FLOAT);
private void getShadowStrength(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || shadowStrength != 1.0F) {
@@ -184,7 +181,7 @@ public abstract class RDisplay extends REntity {
sendPacket(updatePacketSink, this::getDisplayWidth);
}
private static final Object displayWidthWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 20 : 19, Float.class);
private static final EntityDataAccessor<Float> displayWidthWatcher = new EntityDataAccessor<>(20, EntityDataSerializers.FLOAT);
private void getDisplayWidth(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || displayWidth != 0.0F) {
@@ -197,7 +194,7 @@ public abstract class RDisplay extends REntity {
sendPacket(updatePacketSink, this::getDisplayHeight);
}
private static final Object displayHeightWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 21 : 20, Float.class);
private static final EntityDataAccessor<Float> displayHeightWatcher = new EntityDataAccessor<>(21, EntityDataSerializers.FLOAT);
private void getDisplayHeight(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || displayHeight != 0.0F) {
@@ -210,7 +207,7 @@ public abstract class RDisplay extends REntity {
sendPacket(updatePacketSink, this::getInterpolationDelay);
}
private static final Object interpolationDelayWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 8 : 7, Integer.class);
private static final EntityDataAccessor<Integer> interpolationDelayWatcher = new EntityDataAccessor<>(8, EntityDataSerializers.INT);
private void getInterpolationDelay(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || interpolationDelay != 0) {
@@ -223,7 +220,7 @@ public abstract class RDisplay extends REntity {
sendPacket(updatePacketSink, this::getBillboard);
}
private static final Object billboardWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 15 : 14, Byte.class);
private static final EntityDataAccessor<Byte> billboardWatcher = new EntityDataAccessor<>(15, EntityDataSerializers.BYTE);
private void getBillboard(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || billboard != Display.Billboard.FIXED) {
@@ -236,7 +233,7 @@ public abstract class RDisplay extends REntity {
sendPacket(updatePacketSink, this::getGlowColorOverride);
}
private static final Object glowColorOverrideWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 22 : 21, Integer.class);
private static final EntityDataAccessor<Integer> glowColorOverrideWatcher = new EntityDataAccessor<>(22, EntityDataSerializers.INT);
private void getGlowColorOverride(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || glowColorOverride != null) {
@@ -249,7 +246,7 @@ public abstract class RDisplay extends REntity {
sendPacket(updatePacketSink, this::getBrightness);
}
private static final Object brightnessWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 16 : 15, Integer.class);
private static final EntityDataAccessor<Integer> brightnessWatcher = new EntityDataAccessor<>(16, EntityDataSerializers.INT);
private void getBrightness(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || brightness != null) {
@@ -19,12 +19,26 @@
package de.steamwar.entity;
import com.mojang.datafixers.util.Pair;
import de.steamwar.Reflection;
import de.steamwar.core.*;
import de.steamwar.core.BountifulWrapper;
import de.steamwar.core.FlatteningWrapper;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.*;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Location;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
@@ -34,13 +48,11 @@ import java.util.function.Function;
public class REntity {
private static final Object entityStatusWatcher = BountifulWrapper.impl.getDataWatcherObject(0, Byte.class);
private static final Object sneakingDataWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() > 12 ? 6 : 0, FlatteningWrapper.impl.getPose(FlatteningWrapper.EntityPose.NORMAL).getClass());
private static final Object bowDrawnWatcher = Core.getVersion() >= 21 ? BountifulWrapper.impl.getDataWatcherObject(6, FlatteningWrapper.impl.getPose(FlatteningWrapper.EntityPose.NORMAL).getClass()) : BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() > 12 ? 7 : 6, Byte.class);
private static final Object nameWatcher = BountifulWrapper.impl.getDataWatcherObject(2, Core.getVersion() > 12 ? Optional.class : String.class); // Optional<IChatBaseComponent>
private static final Object nameVisibleWatcher = BountifulWrapper.impl.getDataWatcherObject(3, Boolean.class);
private static final Object noGravityDataWatcher = BountifulWrapper.impl.getDataWatcherObject(5,Boolean.class);
private static final EntityDataAccessor<Byte> entityStatusWatcher = new EntityDataAccessor<>(0, EntityDataSerializers.BYTE);
private static final EntityDataAccessor<Pose> poseDataWatcher = new EntityDataAccessor<>(6, EntityDataSerializers.POSE);
private static final EntityDataAccessor<Optional<Component>> nameWatcher = new EntityDataAccessor<>(2, EntityDataSerializers.OPTIONAL_COMPONENT);
private static final EntityDataAccessor<Boolean> nameVisibleWatcher = new EntityDataAccessor<>(3, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Boolean> noGravityDataWatcher = new EntityDataAccessor<>(5, EntityDataSerializers.BOOLEAN);
private static int entityIdCounter = -1;
private static final Random random = new Random();
@@ -67,11 +79,12 @@ public class REntity {
@Getter
private boolean invisible;
@Getter
private FlatteningWrapper.EntityPose pose = FlatteningWrapper.EntityPose.NORMAL;
private Pose pose = Pose.STANDING;
@Getter
private boolean bowDrawn;
@Getter
private boolean noGravity;
@Getter
private boolean isGlowing;
private int fireTick;
@@ -81,15 +94,15 @@ public class REntity {
protected final Map<Object, ItemStack> itemSlots;
public REntity(REntityServer server, EntityType entityType, Location location) {
this(server,entityType,location,0);
this(server, entityType, location, 0);
server.addEntity(this);
}
protected REntity(REntityServer server, EntityType entityType, Location location,int objectData) {
this(server, entityType, new UUID(random.nextLong() & -61441L | 16384L, random.nextLong() & 4611686018427387903L | -9223372036854775808L), location,objectData);
protected REntity(REntityServer server, EntityType entityType, Location location, int objectData) {
this(server, entityType, new UUID(random.nextLong() & -61441L | 16384L, random.nextLong() & 4611686018427387903L | -9223372036854775808L), location, objectData);
}
protected REntity(REntityServer server, EntityType entityType, UUID uuid, Location location,int objectData) {
protected REntity(REntityServer server, EntityType entityType, UUID uuid, Location location, int objectData) {
this.server = server;
this.entityType = entityType;
this.entityId = entityIdCounter--;
@@ -110,10 +123,9 @@ public class REntity {
}
public void hide(boolean hide) {
if(hidden == hide)
return;
if (hidden == hide) return;
if(hide) {
if (hide) {
despawn(packet -> server.updateEntity(this, packet));
hidden = true;
} else {
@@ -126,7 +138,8 @@ public class REntity {
move(location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), rotToByte(location.getYaw()));
}
private static final double MAX_REL_MOVE = Core.getVersion() > 8 ? 8.0 : 4.0;
private static final double MAX_REL_MOVE = 8.0;
public void move(double locX, double locY, double locZ, float pitch, float yaw, byte headYaw) {
server.preEntityMove(this, locX, locZ);
@@ -143,16 +156,17 @@ public class REntity {
this.yaw = rotToByte(yaw);
this.pitch = rotToByte(pitch);
if(Math.abs(diffX) < MAX_REL_MOVE && Math.abs(diffY) < MAX_REL_MOVE && Math.abs(diffZ) < MAX_REL_MOVE) {
Object packet = getMoveLookPacket(diffX, diffY, diffZ,rotEq);
if (Math.abs(diffX) < MAX_REL_MOVE && Math.abs(diffY) < MAX_REL_MOVE && Math.abs(diffZ) < MAX_REL_MOVE) {
Object packet = getMoveLookPacket(diffX, diffY, diffZ, rotEq);
if(packet != null)
if (packet != null) {
server.updateEntity(this, packet);
}
} else {
server.updateEntity(this, getTeleportPacket());
}
if(this.headYaw != headYaw) {
if (this.headYaw != headYaw) {
this.headYaw = headYaw;
server.updateEntity(this, getHeadRotationPacket());
}
@@ -160,9 +174,10 @@ public class REntity {
server.postEntityMove(this, fromX, fromZ);
}
private static final Class<?> animationPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundAnimatePacket");
private static final Reflection.Field<Integer> animationEntity = Reflection.getField(animationPacket, int.class, Core.getVersion() > 15 ? (Core.getVersion() > 19 ? 5 : 6) : 0);
private static final Reflection.Field<Integer> animationAnimation = Reflection.getField(animationPacket, int.class, Core.getVersion() > 15 ? (Core.getVersion() > 19 ? 6 : 7) : 1);
private static final Class<?> animationPacket = ClientboundAnimatePacket.class;
private static final Reflection.Field<Integer> animationEntity = Reflection.getField(animationPacket, int.class, 5);
private static final Reflection.Field<Integer> animationAnimation = Reflection.getField(animationPacket, int.class, 6);
public void showAnimation(byte animation) {
Object packet = Reflection.newInstance(animationPacket);
animationEntity.set(packet, entityId);
@@ -170,23 +185,14 @@ public class REntity {
server.updateEntity(this, packet);
}
private static final Class<?> velocityPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket");
private static final Reflection.Field<Integer> velocityEntity = Reflection.getField(velocityPacket, int.class, 0);
private static final Reflection.Field<Integer> velocityX = Reflection.getField(velocityPacket, int.class, 1);
private static final Reflection.Field<Integer> velocityY = Reflection.getField(velocityPacket, int.class, 2);
private static final Reflection.Field<Integer> velocityZ = Reflection.getField(velocityPacket, int.class, 3);
public void setVelocity(double dX, double dY, double dZ) {
Object packet = Reflection.newInstance(velocityPacket);
velocityEntity.set(packet, entityId);
velocityX.set(packet, calcVelocity(dX));
velocityY.set(packet, calcVelocity(dY));
velocityZ.set(packet, calcVelocity(dZ));
server.updateEntity(this, packet);
server.updateEntity(this, new ClientboundSetEntityMotionPacket(entityId, new Vec3(calcVelocity(dX), calcVelocity(dY), calcVelocity(dZ))));
}
private static final Class<?> statusPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundEntityEventPacket");
private static final Class<?> statusPacket = ClientboundEntityEventPacket.class;
private static final Reflection.Field<Integer> statusEntity = Reflection.getField(statusPacket, int.class, 0);
private static final Reflection.Field<Byte> statusStatus = Reflection.getField(statusPacket, byte.class, 0);
public void showDamage() {
Object packet = Reflection.newInstance(statusPacket);
statusEntity.set(packet, entityId);
@@ -194,13 +200,9 @@ public class REntity {
server.updateEntity(this, packet);
}
public void setPose(FlatteningWrapper.EntityPose pose) {
public void setPose(Pose pose) {
this.pose = pose;
if(Core.getVersion() > 12) {
server.updateEntity(this, getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(pose)));
} else {
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
}
server.updateEntity(this, getDataWatcherPacket(poseDataWatcher, pose));
}
public void setOnFire(boolean perma) {
@@ -219,19 +221,13 @@ public class REntity {
public void setBowDrawn(boolean drawn, boolean offHand) {
bowDrawn = drawn;
if (Core.getVersion() >= 21) {
server.updateEntity(this, getDataWatcherPacket(bowDrawnWatcher, FlatteningWrapper.impl.getPose(FlatteningWrapper.EntityPose.SHOOTING)));
} else if(Core.getVersion() > 8){
server.updateEntity(this, getDataWatcherPacket(bowDrawnWatcher, (byte) ((drawn ? 1 : 0) + (offHand ? 2 : 0))));
}else{
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
}
server.updateEntity(this, getDataWatcherPacket(poseDataWatcher, Pose.SHOOTING));
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
server.updateEntity(this, getDataWatcherPacket(
nameWatcher, FlatteningWrapper.impl.formatDisplayName(displayName),
nameWatcher, FlatteningWrapper.formatDisplayName(displayName),
nameVisibleWatcher, displayName != null
));
}
@@ -247,55 +243,18 @@ public class REntity {
public void setNoGravity(boolean noGravity) {
this.noGravity = noGravity;
if(Core.getVersion() > 8)
server.updateEntity(this,getDataWatcherPacket(noGravityDataWatcher,noGravity));
server.updateEntity(this, getDataWatcherPacket(noGravityDataWatcher, noGravity));
}
public void setGlowing(boolean glowing) {
this.isGlowing = glowing;
if(Core.getVersion() > 8) {
server.updateEntity(this,getDataWatcherPacket(entityStatusWatcher,getEntityStatus()));
}
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
}
public boolean isGlowing() {
return isGlowing;
}
private static final Function<REntity, Object> spawnPacketGenerator = entitySpawnPacketGenerator(ClientboundAddEntityPacket.class, 2);
private static final Reflection.Field<Integer> additionalData = Reflection.getField(ClientboundAddEntityPacket.class, int.class, 4);
private static int spawnPacketOffset() {
switch (Core.getVersion()) {
case 8:
case 18:
return 1;
case 9:
case 10:
case 12:
case 14:
case 15:
return 0;
case 19:
default:
return 2;
}
}
private static final Function<REntity, Object> spawnPacketGenerator = entitySpawnPacketGenerator(ProtocolWrapper.spawnPacket, spawnPacketOffset());
private static int objectDataOffset() {
switch (Core.getVersion()) {
case 8:
return 9;
case 9:
case 14:
case 12:
case 10:
case 15:
case 18:
return 6;
case 19:
default:
return 4;
}
}
private static final Reflection.Field<Integer> additionalData = Reflection.getField(ProtocolWrapper.spawnPacket, int.class, objectDataOffset());
private Object spawnPacketGenerator() {
Object packet = spawnPacketGenerator.apply(this);
additionalData.set(packet, objectData);
@@ -306,60 +265,45 @@ public class REntity {
// empty for regular entity
}
private static final Function<REntity, Object> livingSpawnPacketGenerator = Core.getVersion() >= 19 ? REntity::spawnPacketGenerator : entitySpawnPacketGenerator(ProtocolWrapper.spawnLivingPacket, Core.getVersion() == 8 ? 2 : 0);
void spawn(Consumer<Object> packetSink) {
if(entityType.isAlive()) {
packetSink.accept(livingSpawnPacketGenerator.apply(this));
} else {
packetSink.accept(spawnPacketGenerator());
}
packetSink.accept(spawnPacketGenerator());
postSpawn(packetSink);
}
protected void postSpawn(Consumer<Object> packetSink) {
if(headYaw != 0) {
if (headYaw != 0) {
packetSink.accept(getHeadRotationPacket());
}
if(Core.getVersion() > 12 && pose != FlatteningWrapper.EntityPose.NORMAL) {
packetSink.accept(getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(pose)));
if (pose != Pose.STANDING) {
packetSink.accept(getDataWatcherPacket(poseDataWatcher, pose));
}
byte status = getEntityStatus();
if(status != 0) {
if (status != 0) {
packetSink.accept(getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
}
if(displayName != null) {
packetSink.accept(getDataWatcherPacket(nameWatcher, FlatteningWrapper.impl.formatDisplayName(displayName), nameVisibleWatcher, true));
if (displayName != null) {
packetSink.accept(getDataWatcherPacket(nameWatcher, FlatteningWrapper.formatDisplayName(displayName), nameVisibleWatcher, true));
}
if(Core.getVersion() > 8 && noGravity)
if (noGravity) {
packetSink.accept(getDataWatcherPacket(noGravityDataWatcher, true));
}
}
void tick() {
if(fireTick > 0) {
if (fireTick > 0) {
fireTick--;
if(fireTick == 0) {
if (fireTick == 0) {
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
}
}
}
private static final Class<?> destroyPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket");
private static final Reflection.Field<?> destroyEntities;
static {
if(Core.getVersion() > 15)
destroyEntities = Reflection.getField(destroyPacket, IntList.class, 0);
else
destroyEntities = Reflection.getField(destroyPacket, int[].class, 0);
}
void despawn(Consumer<Object> packetSink){
Object packet = Reflection.newInstance(destroyPacket);
destroyEntities.set(packet, Core.getVersion() > 15 ? new IntArrayList(new int[]{entityId}) : new int[]{entityId});
packetSink.accept(packet);
void despawn(Consumer<Object> packetSink) {
packetSink.accept(new ClientboundRemoveEntitiesPacket(new IntArrayList(new int[]{entityId})));
}
void delist(Consumer<Object> packetSink) {
@@ -377,86 +321,61 @@ public class REntity {
private byte getEntityStatus() {
byte status = 0;
if(fireTick != 0)
status |= 1;
if(pose == FlatteningWrapper.EntityPose.SNEAKING)
status |= 2;
if(Core.getVersion() == 8 && bowDrawn)
status |= 0x10;
if(invisible)
status |= 0x20;
if(Core.getVersion() > 8 && isGlowing)
status |= 0x40;
if (fireTick != 0) status |= 1;
if (pose == Pose.CROUCHING) status |= 2;
if (invisible) status |= 0x20;
if (isGlowing) status |= 0x40;
return status;
}
protected Object getDataWatcherPacket(Object... dataWatcherKeyValues) {
return ChatWrapper.impl.getDataWatcherPacket(entityId, dataWatcherKeyValues);
}
public static final Class<?> teleportPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket");
public static final Reflection.Field<Integer> teleportEntity = Reflection.getField(teleportPacket, int.class, 0);
public static final BountifulWrapper.PositionSetter teleportPosition = BountifulWrapper.impl.getPositionSetter(teleportPacket, Core.getVersion() == 8 ? 1 : 0);
private Object getTeleportPacket(){
if (Core.getVersion() >= 21) {
return PacketConstructor.impl.teleportPacket(entityId, x, y, z, pitch, yaw);
ArrayList<SynchedEntityData.DataValue<?>> nativeWatchers = new ArrayList<>(1);
for (int i = 0; i < dataWatcherKeyValues.length; i += 2) {
nativeWatchers.add(((SynchedEntityData.DataItem<?>) BountifulWrapper.impl.getDataWatcherItem(dataWatcherKeyValues[i], dataWatcherKeyValues[i + 1])).value());
}
Object packet = Reflection.newInstance(teleportPacket);
teleportEntity.set(packet, entityId);
teleportPosition.set(packet, x, y, z, pitch, yaw);
return packet;
return new ClientboundSetEntityDataPacket(entityId, nativeWatchers);
}
private Object getTeleportPacket() {
PositionMoveRotation rot = new PositionMoveRotation(new Vec3(x, y, z), Vec3.ZERO, pitch, yaw);
return new ClientboundTeleportEntityPacket(entityId, rot, Collections.emptySet(), false);
}
private static final Class<?> entityPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket");
private static final Reflection.Field<Integer> moveEntityId = Reflection.getField(entityPacket, int.class, 0);
private static final BountifulWrapper.PositionSetter movePosition = BountifulWrapper.impl.getRelMoveSetter(entityPacket);
private static final Class<?> lookPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot");
private static final Class<?> movePacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Pos");
private static final Class<?> moveLookPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$PosRot");
private Object getMoveLookPacket(double diffX, double diffY, double diffZ, boolean rotEq) {
Class<?> clazz;
if(diffX == 0 && diffY == 0 && diffZ == 0) {
if(rotEq)
return null;
short x = (short) (this.x * 4096);
short y = (short) (this.y * 4096);
short z = (short) (this.z * 4096);
byte yaw = (byte) (this.yaw * 256 / 360);
byte pitch = (byte) (this.pitch * 256 / 360);
clazz = lookPacket;
if (diffX == 0 && diffY == 0 && diffZ == 0) {
if (rotEq) return null;
return new ClientboundMoveEntityPacket.Rot(entityId, pitch, yaw, false);
} else if (rotEq) {
clazz = movePacket;
return new ClientboundMoveEntityPacket.Pos(entityId, x, y, z, false);
} else {
clazz = moveLookPacket;
return new ClientboundMoveEntityPacket.PosRot(entityId, x, y, z, pitch, yaw, false);
}
Object packet = Reflection.newInstance(clazz);
moveEntityId.set(packet, entityId);
movePosition.set(packet, diffX, diffY, diffZ, pitch, yaw);
return packet;
}
private static final Class<?> headRotationPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundRotateHeadPacket");
private static final Class<?> headRotationPacket = ClientboundRotateHeadPacket.class;
private static final Reflection.Field<Integer> headRotationEntity = Reflection.getField(headRotationPacket, int.class, 0);
private static final Reflection.Field<Byte> headRotationYaw = Reflection.getField(headRotationPacket, byte.class, 0);
private Object getHeadRotationPacket(){
private Object getHeadRotationPacket() {
Object packet = Reflection.newInstance(headRotationPacket);
headRotationEntity.set(packet, entityId);
headRotationYaw.set(packet, headYaw);
return packet;
}
private static final Reflection.Field<Integer> equipmentEntity = Reflection.getField(ProtocolWrapper.equipmentPacket, int.class, 0);
private static final Reflection.Field<List> equipmentSlots = Reflection.getField(ProtocolWrapper.equipmentPacket, List.class, 0);
private static final Class<?> craftItemStack = Reflection.getClass("org.bukkit.craftbukkit.inventory.CraftItemStack");
protected static final Reflection.Method asNMSCopy = Reflection.getTypedMethod(REntity.craftItemStack, "asNMSCopy", ProtocolWrapper.itemStack, ItemStack.class);
protected Object getEquipmentPacket(Object slot, ItemStack stack){
Object packet = Reflection.newInstance(ProtocolWrapper.equipmentPacket);
equipmentEntity.set(packet, entityId);
equipmentSlots.set(packet, new ArrayList<>());
ProtocolWrapper.impl.setEquipmentPacketStack(packet, slot, asNMSCopy.invoke(null, stack));
return packet;
protected Object getEquipmentPacket(Object slot, ItemStack stack) {
return new ClientboundSetEquipmentPacket(entityId, Collections.singletonList(Pair.of((EquipmentSlot) slot, CraftItemStack.asNMSCopy(stack))));
}
private static final Reflection.Field<net.minecraft.world.entity.EntityType> spawnType = Reflection.getField(ClientboundAddEntityPacket.class, net.minecraft.world.entity.EntityType.class, 0);
private static Function<REntity, Object> entitySpawnPacketGenerator(Class<?> spawnPacket, int posOffset) {
BountifulWrapper.UUIDSetter uuid = BountifulWrapper.impl.getUUIDSetter(spawnPacket);
Function<REntity, Object> packetGenerator = spawnPacketGenerator(spawnPacket, posOffset);
@@ -464,7 +383,8 @@ public class REntity {
return entity -> {
Object packet = packetGenerator.apply(entity);
uuid.set(packet, entity.uuid);
FlatteningWrapper.impl.setSpawnPacketType(packet, entity.entityType);
ResourceLocation key = CraftNamespacedKey.toMinecraft(entity.entityType.getKey());
spawnType.set(packet, BuiltInRegistries.ENTITY_TYPE.get(key).get().value());
return packet;
};
}
@@ -482,10 +402,10 @@ public class REntity {
}
private byte rotToByte(float rot) {
return (byte)((int)(rot * 256.0F / 360.0F));
return (byte) ((int) (rot * 256.0F / 360.0F));
}
private int calcVelocity(double value) {
return (int)(Math.max(-3.9, Math.min(value, 3.9)) * 8000);
return (int) (Math.max(-3.9, Math.min(value, 3.9)) * 8000);
}
}
@@ -19,10 +19,9 @@
package de.steamwar.entity;
import de.steamwar.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.core.Core;
import de.steamwar.core.FlatteningWrapper;
import net.minecraft.network.protocol.game.ServerboundInteractPacket;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -38,7 +37,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -47,21 +45,6 @@ public class REntityServer implements Listener {
private static final HashSet<REntity> emptyEntities = new HashSet<>(0);
private static final HashSet<Player> emptyPlayers = new HashSet<>(0);
private static final Class<?> useEntity = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundInteractPacket");
private static final Reflection.Field<Integer> useEntityTarget = Reflection.getField(useEntity, int.class, 0);
private static final Class<?> useEntityEnumAction = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundInteractPacket$Action");
private static final Reflection.Field<?> useEntityAction = Reflection.getField(useEntity, useEntityEnumAction, 0);
private static final Function<Object, Integer> getEntityAction;
static {
if(Core.getVersion() > 15) {
Class<?> useEntityEnumActionType = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundInteractPacket$ActionType");
Reflection.Method useEntityGetAction = Reflection.getTypedMethod(useEntityEnumAction, null, useEntityEnumActionType);
getEntityAction = value -> ((Enum<?>) useEntityGetAction.invoke(value)).ordinal();
} else {
getEntityAction = value -> ((Enum<?>) value).ordinal();
}
}
private final ConcurrentHashMap<Integer, REntity> entityMap = new ConcurrentHashMap<>();
private final HashMap<Long, HashSet<REntity>> entities = new HashMap<>();
private final HashMap<Long, Set<Player>> players = new HashMap<>();
@@ -71,16 +54,14 @@ public class REntityServer implements Listener {
private EntityActionListener callback = null;
private final Set<Player> playersThatClicked = Collections.synchronizedSet(new HashSet<>());
private final BiFunction<Player, Object, Object> filter = (player, packet) -> {
REntity entity = entityMap.get(useEntityTarget.get(packet));
if (entity == null)
return packet;
private final BiFunction<Player, ServerboundInteractPacket, Object> filter = (player, packet) -> {
REntity entity = entityMap.get(packet.getEntityId());
if (entity == null) return packet;
if (playersThatClicked.contains(player))
return null;
if (playersThatClicked.contains(player)) return null;
playersThatClicked.add(player);
EntityAction action = getEntityAction.apply(useEntityAction.get(packet)) == 1 ? EntityAction.ATTACK : EntityAction.INTERACT;
EntityAction action = packet.isAttack() ? EntityAction.ATTACK : EntityAction.INTERACT;
Bukkit.getScheduler().runTask(Core.getInstance(), () -> {
playersThatClicked.remove(player);
callback.onAction(player, entity, action);
@@ -96,8 +77,9 @@ public class REntityServer implements Listener {
boolean uninitialized = this.callback == null;
this.callback = callback;
if(uninitialized)
TinyProtocol.instance.addFilter(useEntity, filter);
if (uninitialized) {
TinyProtocol.instance.addTypedFilter(ServerboundInteractPacket.class, filter);
}
}
public void addPlayer(Player player) {
@@ -120,8 +102,8 @@ public class REntityServer implements Listener {
}
public void close() {
TinyProtocol.instance.removeFilter(useEntity, filter);
for(Player player : lastLocation.keySet().toArray(new Player[0])) {
TinyProtocol.instance.removeFilter(ServerboundInteractPacket.class, filter);
for (Player player : lastLocation.keySet().toArray(new Player[0])) {
removePlayer(player);
}
HandlerList.unregisterAll(this);
@@ -137,11 +119,11 @@ public class REntityServer implements Listener {
void preEntityMove(REntity entity, double toX, double toZ) {
long fromId = entityToId(entity);
long toId = posToId(toX, toZ);
if(fromId == toId)
return;
if (fromId == toId) return;
if(!entity.isHidden())
if (!entity.isHidden()) {
onMissing(players.get(fromId), players.get(toId), entity::despawn);
}
onMissing(players.get(fromId), players.get(toId), entity::delist);
removeEntityFromChunk(entity);
}
@@ -149,20 +131,19 @@ public class REntityServer implements Listener {
void postEntityMove(REntity entity, double fromX, double fromZ) {
long fromId = posToId(fromX, fromZ);
long toId = entityToId(entity);
if(fromId == toId)
return;
if (fromId == toId) return;
addEntityToChunk(entity);
onMissing(players.get(toId), players.get(fromId), entity::list);
if(!entity.isHidden())
if (!entity.isHidden()) {
onMissing(players.get(toId), players.get(fromId), entity::spawn);
}
}
void updateEntity(REntity entity, Object packet) {
if(entity.isHidden())
return;
if (entity.isHidden()) return;
for(Player player : players.getOrDefault(entityToId(entity), emptyPlayers)) {
for (Player player : players.getOrDefault(entityToId(entity), emptyPlayers)) {
TinyProtocol.instance.sendPacket(player, packet);
}
}
@@ -191,8 +172,7 @@ public class REntityServer implements Listener {
HashSet<REntity> entitiesInChunk = entities.get(id);
if (entitiesInChunk == null) return;
entitiesInChunk.remove(entity);
if(entitiesInChunk.isEmpty())
entities.remove(id);
if (entitiesInChunk.isEmpty()) entities.remove(id);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
@@ -200,28 +180,26 @@ public class REntityServer implements Listener {
Player player = e.getPlayer();
Location from = lastLocation.get(player);
Location to = e.getTo();
if(from == null || to == null)
return;
if (from == null || to == null) return;
int fromX = posToChunk(from.getX());
int fromZ = posToChunk(from.getZ());
int toX = posToChunk(to.getX());
int toZ = posToChunk(to.getZ());
if(fromX == toX && fromZ == toZ)
return;
if (fromX == toX && fromZ == toZ) return;
lastLocation.put(player, to);
int toViewDistance = viewRadius(player);
forChunkInView(player, from, (x, z) -> {
if(Math.abs(x - toX) > toViewDistance || Math.abs(z - toZ) > toViewDistance) {
if (Math.abs(x - toX) > toViewDistance || Math.abs(z - toZ) > toViewDistance) {
removePlayerFromChunk(player, x, z);
}
});
int fromViewDistance = this.viewDistance.put(player, toViewDistance);
forChunkInView(player, to, (x, z) -> {
if(Math.abs(x - fromX) > fromViewDistance || Math.abs(z - fromZ) > fromViewDistance) {
if (Math.abs(x - fromX) > fromViewDistance || Math.abs(z - fromZ) > fromViewDistance) {
addPlayerToChunk(player, x, z);
}
});
@@ -231,25 +209,22 @@ public class REntityServer implements Listener {
public void onQuit(PlayerQuitEvent e) {
Player player = e.getPlayer();
Location location = lastLocation.remove(player);
if(location == null)
return;
if (location == null) return;
forChunkInView(player, location, (x, z) -> {
long id = chunkToId(x, z);
Set<Player> playersInChunk = players.get(id);
playersInChunk.remove(player);
if(playersInChunk.isEmpty())
players.remove(id);
if (playersInChunk.isEmpty()) players.remove(id);
});
viewDistance.remove(player);
}
private void onMissing(Set<Player> of, Set<Player> in, Consumer<Consumer<Object>> packetProvider) {
if(of == null)
return;
if (of == null) return;
for(Player player : of) {
if(in == null || !in.contains(player)) {
for (Player player : of) {
if (in == null || !in.contains(player)) {
packetProvider.accept(packet -> TinyProtocol.instance.sendPacket(player, packet));
}
}
@@ -260,8 +235,8 @@ public class REntityServer implements Listener {
int chunkZ = posToChunk(location.getZ());
int viewDistance = this.viewDistance.get(player);
for(int x = chunkX - viewDistance; x <= chunkX + viewDistance; x++) {
for(int z = chunkZ - viewDistance; z <= chunkZ + viewDistance; z++) {
for (int x = chunkX - viewDistance; x <= chunkX + viewDistance; x++) {
for (int z = chunkZ - viewDistance; z <= chunkZ + viewDistance; z++) {
func.accept(x, z);
}
}
@@ -270,10 +245,11 @@ public class REntityServer implements Listener {
private void addPlayerToChunk(Player player, int x, int z) {
long id = chunkToId(x, z);
players.computeIfAbsent(id, i -> new HashSet<>()).add(player);
for(REntity entity : entities.getOrDefault(id, emptyEntities)) {
for (REntity entity : entities.getOrDefault(id, emptyEntities)) {
entity.list(packet -> TinyProtocol.instance.sendPacket(player, packet));
if(!entity.isHidden())
if (!entity.isHidden()) {
entity.spawn(packet -> TinyProtocol.instance.sendPacket(player, packet));
}
}
}
@@ -282,12 +258,12 @@ public class REntityServer implements Listener {
Set<Player> playersInChunk = players.get(id);
playersInChunk.remove(player);
if(playersInChunk.isEmpty())
players.remove(id);
if (playersInChunk.isEmpty()) players.remove(id);
for(REntity entity : entities.getOrDefault(id, emptyEntities)) {
if(!entity.isHidden())
for (REntity entity : entities.getOrDefault(id, emptyEntities)) {
if (!entity.isHidden()) {
entity.despawn(packet -> TinyProtocol.instance.sendPacket(player, packet));
}
entity.delist(packet -> TinyProtocol.instance.sendPacket(player, packet));
}
}
@@ -299,11 +275,11 @@ public class REntityServer implements Listener {
}
private int posToChunk(double coord) {
return (int)(coord / 16) - (coord < 0 ? 1 : 0);
return (int) (coord / 16) - (coord < 0 ? 1 : 0);
}
private int viewRadius(Player player) {
return FlatteningWrapper.impl.getViewDistance(player) / 2;
return player.getClientViewDistance() / 2;
}
private long entityToId(REntity entity) {
@@ -19,7 +19,6 @@
package de.steamwar.entity;
import de.steamwar.core.Core;
import de.steamwar.techhider.BlockIds;
import lombok.Getter;
import org.bukkit.Location;
@@ -27,12 +26,12 @@ import org.bukkit.Material;
import org.bukkit.entity.EntityType;
@Getter
public class RFallingBlockEntity extends REntity{
public class RFallingBlockEntity extends REntity {
private final Material material;
public RFallingBlockEntity(REntityServer server, Location location, Material material) {
super(server, EntityType.FALLING_BLOCK, location, BlockIds.impl.materialToId(material) >> (Core.getVersion() <= 12 ? 4 : 0));
super(server, EntityType.FALLING_BLOCK, location, BlockIds.impl.materialToId(material));
this.material = material;
server.addEntity(this);
}
@@ -19,12 +19,12 @@
package de.steamwar.entity;
import de.steamwar.core.BountifulWrapper;
import de.steamwar.core.Core;
import de.steamwar.core.ProtocolWrapper;
import lombok.Getter;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ItemDisplay;
import org.bukkit.inventory.ItemStack;
@@ -61,14 +61,16 @@ public class RItemDisplay extends RDisplay {
sendPacket(updatePacketSink, this::getItemStack);
}
private static final Object itemStackWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 23 : 22, ProtocolWrapper.itemStack);
private static final EntityDataAccessor<net.minecraft.world.item.ItemStack> itemStackWatcher = new EntityDataAccessor<>(23, EntityDataSerializers.ITEM_STACK);
private void getItemStack(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || !itemStack.equals(DEFAULT_ITEM_STACK)) {
packetSink.accept(itemStackWatcher, asNMSCopy.invoke(null, itemStack));
packetSink.accept(itemStackWatcher, CraftItemStack.asNMSCopy(itemStack));
}
}
private static final Object itemDisplayTransformWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 24 : 23, Byte.class);
private static final EntityDataAccessor<Byte> itemDisplayTransformWatcher = new EntityDataAccessor<>(24, EntityDataSerializers.BYTE);
public void setItemDisplayTransform(ItemDisplay.ItemDisplayTransform itemDisplayTransform) {
this.itemDisplayTransform = itemDisplayTransform;
sendPacket(updatePacketSink, this::getItemDisplayTransform);
@@ -21,12 +21,14 @@ package de.steamwar.entity;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import de.steamwar.Reflection;
import de.steamwar.core.*;
import de.steamwar.core.BountifulWrapper;
import de.steamwar.core.ProtocolWrapper;
import de.steamwar.network.CoreNetworkHandler;
import de.steamwar.network.NetworkSender;
import de.steamwar.network.packets.common.PlayerSkinRequestPacket;
import lombok.Getter;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.world.phys.Vec3;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
@@ -36,31 +38,10 @@ import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;
public class RPlayer extends REntity {
private static int skinPartsIndex() {
switch(Core.getVersion()) {
case 8:
return 10;
case 9:
return 12;
case 10:
case 12:
return 13;
case 14:
return 15;
case 15:
return 16;
case 18:
case 19:
default:
return 17;
}
}
private static final Object skinPartsDataWatcher = BountifulWrapper.impl.getDataWatcherObject(skinPartsIndex(), Byte.class);
private static final Object skinPartsDataWatcher = BountifulWrapper.impl.getDataWatcherObject(17, Byte.class);
@Getter
private final UUID actualUUID;
@@ -68,7 +49,7 @@ public class RPlayer extends REntity {
private final String name;
public RPlayer(REntityServer server, UUID uuid, String name, Location location) {
super(server, EntityType.PLAYER, UUID.nameUUIDFromBytes(uuid.toString().getBytes(StandardCharsets.UTF_8)), location,0);
super(server, EntityType.PLAYER, UUID.nameUUIDFromBytes(uuid.toString().getBytes(StandardCharsets.UTF_8)), location, 0);
this.actualUUID = uuid;
this.name = name;
server.addEntity(this);
@@ -79,7 +60,7 @@ public class RPlayer extends REntity {
NetworkSender.sendOrQueue(new PlayerSkinRequestPacket(actualUUID));
return new Property("textures", null, null);
});
if (TrickyTrialsWrapper.impl.getValue(skinData) != null) {
if (skinData.value() != null) {
GameProfile gameProfile = new GameProfile(uuid, name);
gameProfile.getProperties().put("textures", skinData);
return gameProfile;
@@ -93,7 +74,7 @@ public class RPlayer extends REntity {
@Override
void list(Consumer<Object> packetSink) {
saved = getGameProfile();
packetSink.accept(ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, saved, GameMode.CREATIVE));
packetSink.accept(ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, saved, GameMode.CREATIVE));
}
@Override
@@ -111,27 +92,22 @@ public class RPlayer extends REntity {
@Override
void delist(Consumer<Object> packetSink) {
if (saved == null) saved = getGameProfile();
packetSink.accept(ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, saved, GameMode.CREATIVE));
}
private static Class<?> namedSpawnPacket = null;
private static Function<REntity, Object> namedSpawnPacketGenerator = null;
private static Reflection.Field<UUID> namedSpawnUUID = null;
static {
try {
namedSpawnPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundAddPlayerPacket");
namedSpawnPacketGenerator = spawnPacketGenerator(namedSpawnPacket, Core.getVersion() == 8 ? 1 : 0);
namedSpawnUUID = Reflection.getField(namedSpawnPacket, UUID.class, 0);
} catch (IllegalArgumentException e) { }
packetSink.accept(ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, saved, GameMode.CREATIVE));
}
private Object getNamedSpawnPacket() {
if (Core.getVersion() >= 21) return PacketConstructor.impl.createRPlayerSpawn(this);
Object packet = namedSpawnPacketGenerator.apply(this);
namedSpawnUUID.set(packet, uuid);
FlatteningWrapper.impl.setNamedSpawnPacketDataWatcher(packet);
return packet;
return new ClientboundAddEntityPacket(
entityId,
uuid,
x,
y,
z,
yaw,
pitch,
net.minecraft.world.entity.EntityType.PLAYER,
0,
Vec3.ZERO,
headYaw
);
}
}
@@ -19,11 +19,12 @@
package de.steamwar.entity;
import de.steamwar.Reflection;
import de.steamwar.core.BountifulWrapper;
import de.steamwar.core.ChatWrapper;
import de.steamwar.core.Core;
import lombok.Getter;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.contents.PlainTextContents;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.TextDisplay;
@@ -74,11 +75,11 @@ public class RTextDisplay extends RDisplay {
sendPacket(updatePacketSink, this::getText);
}
private static final Class<?> iChatBaseComponent = Reflection.getClass("net.minecraft.network.chat.Component");
private static final Object textWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 23 : 22, iChatBaseComponent);
private static final EntityDataAccessor<Component> textWatcher = new EntityDataAccessor<>(23, EntityDataSerializers.COMPONENT);
private void getText(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || !text.isEmpty()) {
packetSink.accept(textWatcher, ChatWrapper.impl.stringToChatComponent(text));
packetSink.accept(textWatcher, MutableComponent.create(PlainTextContents.create(text)));
}
}
@@ -87,7 +88,8 @@ public class RTextDisplay extends RDisplay {
sendPacket(updatePacketSink, this::getLineWidth);
}
private static final Object lineWidthWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 24 : 23, Integer.class);
private static final EntityDataAccessor<Integer> lineWidthWatcher = new EntityDataAccessor<>(24, EntityDataSerializers.INT);
private void getLineWidth(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || lineWidth != 200) {
packetSink.accept(lineWidthWatcher, lineWidth);
@@ -99,7 +101,8 @@ public class RTextDisplay extends RDisplay {
sendPacket(updatePacketSink, this::getTextOpacity);
}
private static final Object textOpacityWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 26 : 25, Byte.class);
private static final EntityDataAccessor<Byte> textOpacityWatcher = new EntityDataAccessor<>(26, EntityDataSerializers.BYTE);
private void getTextOpacity(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || textOpacity != (byte) -1) {
packetSink.accept(textOpacityWatcher, textOpacity);
@@ -121,7 +124,8 @@ public class RTextDisplay extends RDisplay {
sendPacket(updatePacketSink, this::getTextStatus, this::getBackgroundColor);
}
private static final Object backgroundColorWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 25 : 24, Integer.class);
private static final EntityDataAccessor<Integer> backgroundColorWatcher = new EntityDataAccessor<>(25, EntityDataSerializers.INT);
private void getBackgroundColor(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || backgroundColor != null) {
packetSink.accept(backgroundColorWatcher, backgroundColor);
@@ -140,7 +144,8 @@ public class RTextDisplay extends RDisplay {
sendPacket(updatePacketSink, this::getTextStatus);
}
private static final Object textStatusWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 27 : 26, Byte.class);
private static final EntityDataAccessor<Byte> textStatusWatcher = new EntityDataAccessor<>(27, EntityDataSerializers.BYTE);
private void getTextStatus(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
byte status = 0;
@@ -57,7 +57,7 @@ public class SWAnvilInv {
}
public void setItem(Material m, List<String> lore, boolean e) {
setItem(m, (byte)0, lore, e);
setItem(m, (byte) 0, lore, e);
}
public void setItem(Material m, byte meta, List<String> lore, boolean e) {
@@ -82,16 +82,14 @@ public class SWAnvilInv {
}
private List<AnvilGUI.ResponseAction> onResult(Integer slot, AnvilGUI.StateSnapshot state) {
if(slot != AnvilGUI.Slot.OUTPUT) {
if(slot == AnvilGUI.Slot.INPUT_LEFT && leftCallback != null)
leftCallback.run();
if (slot != AnvilGUI.Slot.OUTPUT) {
if (slot == AnvilGUI.Slot.INPUT_LEFT && leftCallback != null) leftCallback.run();
return Collections.emptyList();
}
String s = state.getText();
if(s.startsWith("»"))
s = s.substring(1);
if (s.startsWith("»")) s = s.substring(1);
callback.accept(s);
player.setLevel(0);
return Collections.singletonList(AnvilGUI.ResponseAction.close());
@@ -79,7 +79,7 @@ public class SWInventory implements Listener {
public void setItem(int pos, ItemStack itemStack, InvCallback c) {
inventory.setItem(pos, itemStack);
if(c != null) {
if (c != null) {
callbacks.put(pos, inventoryClickEvent -> c.clicked(inventoryClickEvent.getClick()));
} else {
callbacks.remove(pos);
@@ -94,11 +94,11 @@ public class SWInventory implements Listener {
setItem(pos, item.getItemStack(), item.getCallback());
}
public void setItem(int pos, Material m, String name, InvCallback c){
public void setItem(int pos, Material m, String name, InvCallback c) {
setItem(pos, m, name, new ArrayList<>(), false, c);
}
public void setItem(int pos, Material m, byte meta, String name, InvCallback c){
public void setItem(int pos, Material m, byte meta, String name, InvCallback c) {
setItem(pos, m, meta, name, new ArrayList<>(), false, c);
}
@@ -147,7 +147,7 @@ public class SWInventory implements Listener {
InventoryView view = player.openInventory(inventory);
title = view.getTitle();
Core.getInstance().getLogger().info("[SWINV] Opened " + title + " for " + player.getName());
if(!open) {
if (!open) {
Bukkit.getPluginManager().registerEvents(this, Core.getInstance());
open = true;
}
@@ -156,8 +156,7 @@ public class SWInventory implements Listener {
@EventHandler
public void onInventoryClick(InventoryClickEvent e) {
if (!player.equals(e.getWhoClicked()))
return;
if (!player.equals(e.getWhoClicked())) return;
if (callbacks.containsKey(e.getRawSlot()) && callbacks.get(e.getRawSlot()) != null) {
e.setCancelled(true);
@@ -168,14 +167,13 @@ public class SWInventory implements Listener {
}
@EventHandler
public void onInventoryClose(InventoryCloseEvent e){
if(!player.equals(e.getPlayer()))
return;
public void onInventoryClose(InventoryCloseEvent e) {
if (!player.equals(e.getPlayer())) return;
InventoryClickEvent.getHandlerList().unregister(this);
InventoryCloseEvent.getHandlerList().unregister(this);
Core.getInstance().getLogger().info("[SWINV] " + player.getName() + " closed " + title);
if(callbacks.containsKey(-1)) {
if (callbacks.containsKey(-1)) {
callbacks.get(-1).accept(null);
}
open = false;
@@ -19,16 +19,17 @@
package de.steamwar.inventory;
import com.destroystokyo.paper.profile.PlayerProfile;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import de.steamwar.core.Core;
import de.steamwar.core.FlatteningWrapper;
import de.steamwar.core.TrickyTrialsWrapper;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import java.util.ArrayList;
import java.util.Arrays;
@@ -42,64 +43,66 @@ public class SWItem {
private ItemMeta itemMeta;
private InvCallback callback;
public static SWItem getPlayerSkull(OfflinePlayer player){
public static SWItem getPlayerSkull(OfflinePlayer player) {
return getPlayerSkull(player.getName());
}
public static SWItem getPlayerSkull(String playerName){
public static SWItem getPlayerSkull(String playerName) {
SWItem p = new SWItem();
ItemStack head = FlatteningWrapper.impl.setSkullOwner(playerName);
ItemStack head = new ItemStack(Material.PLAYER_HEAD, 1);
head.editMeta(SkullMeta.class, skullMeta -> {
try {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName.startsWith(".") ? playerName.substring(1) : playerName);
PlayerProfile playerProfile = offlinePlayer.getPlayerProfile();
playerProfile.complete();
skullMeta.setPlayerProfile(playerProfile);
} catch (Exception e) {
// Ignore
}
});
p.setItemStack(head);
return p;
}
public static Material getMaterial(String material){
try{
Material m = FlatteningWrapper.impl.getMaterial(material);
if(m == null)
return Material.BARRIER;
return m;
}catch(IllegalArgumentException e){
return Material.STONE;
public static Material getMaterial(String material) {
try {
return Material.valueOf(material);
} catch (IllegalArgumentException e) {
return Material.BARRIER;
}
}
public static Material getDye(int colorCode){
return FlatteningWrapper.impl.getDye(colorCode);
}
public SWItem() {
itemStack = new ItemStack(Material.AIR);
itemMeta = itemStack.getItemMeta();
hideAttributes();
}
public SWItem(Material material, String name){
this(material, (byte)0, name, new ArrayList<>(), false, null);
public SWItem(Material material, String name) {
this(material, (byte) 0, name, new ArrayList<>(), false, null);
}
public SWItem(Material material, String name, InvCallback c){
this(material, (byte)0, name, new ArrayList<>(), false, c);
public SWItem(Material material, String name, InvCallback c) {
this(material, (byte) 0, name, new ArrayList<>(), false, c);
}
public SWItem(Material material, byte meta, String name){
public SWItem(Material material, byte meta, String name) {
this(material, meta, name, new ArrayList<>(), false, null);
}
public SWItem(Material material, byte meta, String name, InvCallback c){
public SWItem(Material material, byte meta, String name, InvCallback c) {
this(material, meta, name, new ArrayList<>(), false, c);
}
public SWItem(Material material, String name, List<String> lore, boolean enchanted, InvCallback c) {
this(material, (byte)0, name, lore, enchanted, c);
this(material, (byte) 0, name, lore, enchanted, c);
}
@SuppressWarnings("deprecation")
public SWItem(Material material, byte meta, String name, List<String> lore, boolean enchanted, InvCallback c) {
try {
itemStack = new ItemStack(material, 1, (short)0, meta);
itemStack = new ItemStack(material, 1, (short) 0, meta);
} catch (IllegalArgumentException e) {
itemStack = new ItemStack(material, 1);
}
@@ -111,7 +114,7 @@ public class SWItem {
itemMeta.setDisplayName(name);
if (lore != null && !lore.isEmpty()) itemMeta.setLore(lore);
if (enchanted) itemMeta.addEnchant(TrickyTrialsWrapper.impl.getUnbreakingEnchantment(), 10, true);
if (enchanted) itemMeta.addEnchant(Enchantment.UNBREAKING, 10, true);
itemStack.setItemMeta(itemMeta);
}
callback = c;
@@ -120,32 +123,28 @@ public class SWItem {
public static SWItem getItemFromJson(JsonObject itemJson) {
SWItem item = null;
try {
if(itemJson.has("color")) {
item = new SWItem(SWItem.getDye(itemJson.get("color").getAsInt()),
itemJson.has("color")?itemJson.get("color").getAsByte():0,
itemJson.get("title").getAsString());
}else {
item = new SWItem(SWItem.getMaterial(itemJson.get("material").getAsString()), itemJson.get("title").getAsString());
}
}catch (IllegalArgumentException e) {
item = new SWItem(SWItem.getMaterial(itemJson.get("material").getAsString()), itemJson.get("title").getAsString());
} catch (IllegalArgumentException e) {
item = new SWItem(Material.STONE, itemJson.get("title").getAsString());
}
if(itemJson.has("skullOwner")) {
if (itemJson.has("skullOwner")) {
item = SWItem.getPlayerSkull(itemJson.get("skullOwner").getAsString());
item.setName(itemJson.get("title").getAsString());
}
if(itemJson.has("enchanted"))
if (itemJson.has("enchanted")) {
item.setEnchanted(true);
if(itemJson.has("lore")) {
}
if (itemJson.has("lore")) {
List<String> lore = new ArrayList<>();
JsonArray loreArray = itemJson.getAsJsonArray("lore");
loreArray.forEach(jsonElement -> lore.add(jsonElement.getAsString()));
item.setLore(lore);
}
if (itemJson.has("customModelData"))
if (itemJson.has("customModelData")) {
item.setCustomModelData(itemJson.get("customModelData").getAsInt());
}
return item;
}
@@ -207,20 +206,18 @@ public class SWItem {
}
public SWItem setEnchanted(boolean enchanted) {
if (enchanted){
itemMeta.addEnchant(TrickyTrialsWrapper.impl.getUnbreakingEnchantment() , 10, true);
if (enchanted) {
itemMeta.addEnchant(Enchantment.UNBREAKING, 10, true);
} else {
itemMeta.removeEnchant(TrickyTrialsWrapper.impl.getUnbreakingEnchantment());
itemMeta.removeEnchant(Enchantment.UNBREAKING);
}
itemStack.setItemMeta(itemMeta);
return this;
}
public SWItem setCustomModelData(int customModelData) {
if (Core.getVersion() > 12) {
itemMeta.setCustomModelData(customModelData);
itemStack.setItemMeta(itemMeta);
}
itemMeta.setCustomModelData(customModelData);
itemStack.setItemMeta(itemMeta);
return this;
}
@@ -41,11 +41,11 @@ public class SWListInv<T> extends SWInventory {
private int page;
private boolean opened = false;
public SWListInv(Player p, String t, List<SWListEntry<T>> l, ListCallback<T> c){
public SWListInv(Player p, String t, List<SWListEntry<T>> l, ListCallback<T> c) {
this(p, t, true, l, c);
}
public SWListInv(Player p, String t, boolean dynamicSize, List<SWListEntry<T>> l, ListCallback<T> c){
public SWListInv(Player p, String t, boolean dynamicSize, List<SWListEntry<T>> l, ListCallback<T> c) {
super(p, dynamicSize ? dynamicSize(l.size()) : 54, t);
callback = c;
elements = l;
@@ -54,37 +54,37 @@ public class SWListInv<T> extends SWInventory {
}
@Override
public void open(){
public void open() {
opened = true;
inventory.clear();
callbacks.keySet().stream().filter(i -> i >= 0).collect(Collectors.toList()).forEach(callbacks::remove);
if(!callbacks.containsKey(-999)) {
if (!callbacks.containsKey(-999)) {
setCallback(-999, (ClickType click) -> player.closeInventory());
}
if (sizeBiggerMax()) {
if (page != 0) {
setItem(45, new SWItem(SWItem.getDye(10), (byte) 10, Core.MESSAGE.parse("SWLISINV_PREVIOUS_PAGE_ACTIVE", player), (ClickType click) -> {
setItem(45, new SWItem(Material.LIME_DYE, (byte) 10, Core.MESSAGE.parse("SWLISINV_PREVIOUS_PAGE_ACTIVE", player), (ClickType click) -> {
page--;
open();
}).setCustomModelData(CMDs.PREVIOUS_PAGE));
} else {
setItem(45, new SWItem(SWItem.getDye(8), (byte) 8, Core.MESSAGE.parse("SWLISINV_PREVIOUS_PAGE_INACTIVE", player), (ClickType click) -> {
setItem(45, new SWItem(Material.GRAY_DYE, (byte) 8, Core.MESSAGE.parse("SWLISINV_PREVIOUS_PAGE_INACTIVE", player), (ClickType click) -> {
}).setCustomModelData(CMDs.PREVIOUS_PAGE));
}
if (page < elements.size() / 45 - (elements.size() % 45 == 0 ? 1 : 0)) {
setItem(53, new SWItem(SWItem.getDye(10), (byte) 10, Core.MESSAGE.parse("SWLISINV_NEXT_PAGE_ACTIVE", player), (ClickType click) -> {
setItem(53, new SWItem(Material.LIME_DYE, (byte) 10, Core.MESSAGE.parse("SWLISINV_NEXT_PAGE_ACTIVE", player), (ClickType click) -> {
page++;
open();
}).setCustomModelData(CMDs.NEXT_PAGE));
} else {
setItem(53, new SWItem(SWItem.getDye(8), (byte) 8, Core.MESSAGE.parse("SWLISINV_NEXT_PAGE_INACTIVE", player), (ClickType click) -> {
setItem(53, new SWItem(Material.GRAY_DYE, (byte) 8, Core.MESSAGE.parse("SWLISINV_NEXT_PAGE_INACTIVE", player), (ClickType click) -> {
}).setCustomModelData(CMDs.NEXT_PAGE));
}
} else if (!dynamicSize) {
setItem(45, new SWItem(SWItem.getDye(8), (byte) 8, Core.MESSAGE.parse("SWLISINV_PREVIOUS_PAGE_INACTIVE", player), (ClickType click) -> {
setItem(45, new SWItem(Material.GRAY_DYE, (byte) 8, Core.MESSAGE.parse("SWLISINV_PREVIOUS_PAGE_INACTIVE", player), (ClickType click) -> {
}).setCustomModelData(CMDs.PREVIOUS_PAGE));
setItem(53, new SWItem(SWItem.getDye(8), (byte) 8, Core.MESSAGE.parse("SWLISINV_NEXT_PAGE_INACTIVE", player), (ClickType click) -> {
setItem(53, new SWItem(Material.GRAY_DYE, (byte) 8, Core.MESSAGE.parse("SWLISINV_NEXT_PAGE_INACTIVE", player), (ClickType click) -> {
}).setCustomModelData(CMDs.NEXT_PAGE));
}
@@ -109,75 +109,75 @@ public class SWListInv<T> extends SWInventory {
}
@Override
public void setItem(int pos, SWItem item){
public void setItem(int pos, SWItem item) {
super.setItem(pos, item);
if(!opened)
customItems.put(pos, item);
if (!opened) customItems.put(pos, item);
}
public void setCallback(ListCallback<T> c){
public void setCallback(ListCallback<T> c) {
callback = c;
}
public static List<SWListEntry<UUID>> createPlayerList(UUID without){
public static List<SWListEntry<UUID>> createPlayerList(UUID without) {
List<SWListEntry<UUID>> onlinePlayers = new ArrayList<>();
for(Player player : Bukkit.getOnlinePlayers()){
if(without != null && player.getUniqueId().equals(without))
continue;
for (Player player : Bukkit.getOnlinePlayers()) {
if (without != null && player.getUniqueId().equals(without)) continue;
onlinePlayers.add(new SWListEntry<>(SWItem.getPlayerSkull(player), player.getUniqueId()));
}
return onlinePlayers;
}
public static List<SWListEntry<SchematicNode>> getSchemnodeList(SchematicType type, int steamwarUserId){
public static List<SWListEntry<SchematicNode>> getSchemnodeList(SchematicType type, int steamwarUserId) {
List<SWListEntry<SchematicNode>> schemList = new ArrayList<>();
List<SchematicNode> schems;
if(type == null)
if (type == null) {
schems = SchematicNode.getAllSchematicsAccessibleByUser(steamwarUserId);
else
} else {
schems = SchematicNode.getAllAccessibleSchematicsOfType(steamwarUserId, type.toDB());
}
for(SchematicNode s : schems){
for (SchematicNode s : schems) {
Material m;
if(s.getItem().isEmpty())
m = SWItem.getMaterial("CAULDRON_ITEM");
else
if (s.getItem().isEmpty()) {
m = Material.CAULDRON;
} else {
m = SWItem.getMaterial(s.getItem());
SWItem item = new SWItem(m,"§e" + s.getName());
}
SWItem item = new SWItem(m, "§e" + s.getName());
item.setEnchanted(s.isDir());
schemList.add(new SWListEntry<>(item, s));
}
return schemList;
}
private boolean sizeBiggerMax(){
private boolean sizeBiggerMax() {
return dynamicSize ? elements.size() > 54 : elements.size() > 45;
}
private static int dynamicSize(int size){
return (size>45) ? 54 : (size + 9-size%9);
private static int dynamicSize(int size) {
return (size > 45) ? 54 : (size + 9 - size % 9);
}
public interface ListCallback<T>{
public interface ListCallback<T> {
void clicked(ClickType click, T element);
}
public static class SWListEntry<T>{
public static class SWListEntry<T> {
final SWItem item;
final T object;
public SWListEntry(SWItem item, T object){
public SWListEntry(SWItem item, T object) {
this.item = item;
this.object = object;
}
public SWItem getItem(){
public SWItem getItem() {
return item;
}
public T getObject(){
public T getObject() {
return object;
}
}
@@ -83,7 +83,7 @@ public class SchematicSelector {
public void open() {
injectable.onSelectorCreate(this);
if(publicMode == PublicMode.PUBLIC_ONLY) {
if (publicMode == PublicMode.PUBLIC_ONLY) {
this.user = SteamwarUser.byId(0);
}
openList(null);
@@ -103,9 +103,9 @@ public class SchematicSelector {
private void openList(SchematicNode parent) {
lastParent = parent;
List<SchematicNode> nodes = applySorting(filter != null?getFilteredSchematics():getSchematicList(parent));
List<SchematicNode> nodes = applySorting(filter != null ? getFilteredSchematics() : getSchematicList(parent));
if(sdoTrigger) {
if (sdoTrigger) {
sdoTrigger = false;
openList(nodes.get(0));
return;
@@ -113,18 +113,19 @@ public class SchematicSelector {
List<SWListInv.SWListEntry<SchematicNode>> list = new ArrayList<>();
if(depth != 0) {
list.add(new SWListInv.SWListEntry<>(new SWItem(Material.ARROW, Core.MESSAGE.parse("SCHEM_SELECTOR_BACK", player), clickType -> {}).setCustomModelData(CMDs.BACK), null));
if (depth != 0) {
list.add(new SWListInv.SWListEntry<>(new SWItem(Material.ARROW, Core.MESSAGE.parse("SCHEM_SELECTOR_BACK", player), clickType -> {
}).setCustomModelData(CMDs.BACK), null));
}
for (SchematicNode node : nodes) {
if(node.getName().equals("//copy")) continue;
if (node.getName().equals("//copy")) continue;
list.add(renderItem(node));
}
SWListInv<SchematicNode> inv = new SWListInv<>(player, MessageFormat.format(injectable.createTitle(player), target.target.getName(player), (filter == null || filter.getName() == null)?(parent == null?"/":parent.generateBreadcrumbs(user)):filter.getName()), false, list, (clickType, node) -> handleClick(node, parent));
if(publicMode == PublicMode.ALL) {
if(user.getId() == 0) {
SWListInv<SchematicNode> inv = new SWListInv<>(player, MessageFormat.format(injectable.createTitle(player), target.target.getName(player), (filter == null || filter.getName() == null) ? (parent == null ? "/" : parent.generateBreadcrumbs(user)) : filter.getName()), false, list, (clickType, node) -> handleClick(node, parent));
if (publicMode == PublicMode.ALL) {
if (user.getId() == 0) {
inv.setItem(48, new SWItem(Material.BUCKET, Core.MESSAGE.parse("SCHEM_SELECTOR_OWN", player), clickType -> {
this.user = SteamwarUser.get(player.getUniqueId());
openList(null);
@@ -136,21 +137,21 @@ public class SchematicSelector {
}).setCustomModelData(CMDs.Schematic.PUBLIC_SCHEMS));
}
}
if(target.target.dirs) {
inv.setItem(49, SWItem.getDye(10), Core.MESSAGE.parse("SCHEM_SELECTOR_SEL_DIR", player), clickType -> {
if (target.target.dirs) {
inv.setItem(49, Material.LIME_DYE, Core.MESSAGE.parse("SCHEM_SELECTOR_SEL_DIR", player), clickType -> {
player.closeInventory();
callback.accept(parent);
});
}
if(user.getId() != 0) {
if (user.getId() != 0) {
inv.setItem(50, new SWItem(Material.CHEST, Core.MESSAGE.parse("SCHEM_SELECTOR_NEW_DIR", player), clickType -> createFolderIn(parent)).setCustomModelData(CMDs.Schematic.NEW_DIR));
}
inv.setItem(51, new SWItem(Material.NAME_TAG, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER", player), clickType -> openFilter()).setCustomModelData(CMDs.Schematic.FILTER));
inv.setItem(47, new SWItem(sorting.mat, Core.MESSAGE.parse("SCHEM_SELECTOR_SORTING", player), Arrays.asList(
Core.MESSAGE.parse("SCHEM_SELECTOR_SORTING_CURRENT", player, sorting.parseName(player)),
Core.MESSAGE.parse("SCHEM_SELECTOR_SORTING_DIRECTION", player, Core.MESSAGE.parse(invertSorting?"SCHEM_SELECTOR_SORTING_DSC":"SCHEM_SELECTOR_SORTING_ASC", player))
Core.MESSAGE.parse("SCHEM_SELECTOR_SORTING_DIRECTION", player, Core.MESSAGE.parse(invertSorting ? "SCHEM_SELECTOR_SORTING_DSC" : "SCHEM_SELECTOR_SORTING_ASC", player))
), invertSorting, click -> {
if(click.isLeftClick()) {
if (click.isLeftClick()) {
cycleSorting();
} else {
invertSorting = !invertSorting;
@@ -163,13 +164,13 @@ public class SchematicSelector {
}
private SchematicNode dirUp(SchematicNode parent) {
if(parent == null) {
if (parent == null) {
// Gracefully handle unexpected Updir in Root Folder
depth = 0;
return null;
}
if(!singleDirOpen) {
if(NodeMember.getNodeMember(parent.getId(), user.getId()) != null) {
if (!singleDirOpen) {
if (NodeMember.getNodeMember(parent.getId(), user.getId()) != null) {
return NodeMember.getNodeMember(parent.getId(), user.getId()).getParent().map(integer -> SchematicNode.byIdAndUser(user, integer)).orElse(null);
} else {
return getParent(parent).orElse(null);
@@ -179,7 +180,7 @@ public class SchematicSelector {
do {
sdoTrigger = false;
currentParent = currentParent.flatMap(this::getParent);
if(!currentParent.isPresent()) {
if (!currentParent.isPresent()) {
break;
}
getSchematicList(currentParent.get());
@@ -189,13 +190,13 @@ public class SchematicSelector {
}
private void handleClick(SchematicNode node, SchematicNode parent) {
if(node == null) {
if (node == null) {
depth--;
openList(dirUp(parent));
return;
}
if(node.isDir()) {
if(filter != null && target.target.dirs) {
if (node.isDir()) {
if (filter != null && target.target.dirs) {
player.closeInventory();
callback.accept(node);
return;
@@ -211,19 +212,19 @@ public class SchematicSelector {
private void cycleSorting() {
int next = sorting.ordinal() + 1;
if(next >= all_sortings.length) {
if (next >= all_sortings.length) {
next = 0;
}
sorting = all_sortings[next];
}
private List<SchematicNode> applySorting(List<SchematicNode> nodes) {
if(sorting == Sorting.NAME && !invertSorting) {
if (sorting == Sorting.NAME && !invertSorting) {
return nodes;
}
Comparator<SchematicNode> comparator = sorting.comparator;
if(invertSorting) {
if (invertSorting) {
comparator = comparator.reversed();
}
@@ -234,15 +235,15 @@ public class SchematicSelector {
private SWListInv.SWListEntry<SchematicNode> renderItem(SchematicNode node) {
Material m = SWItem.getMaterial(node.getItem());
String name = Core.MESSAGE.parse((filter != null && filter.name == null)?"SCHEM_SELECTOR_ITEM_NAME":"SCHEM_SELECTOR_ITEM_NAME_FILTER", player, node.getName());
String name = Core.MESSAGE.parse((filter != null && filter.name == null) ? "SCHEM_SELECTOR_ITEM_NAME" : "SCHEM_SELECTOR_ITEM_NAME_FILTER", player, node.getName());
if(filter != null && filter.getName() != null) {
if (filter != null && filter.getName() != null) {
name = name.replace(filter.getName(), Core.MESSAGE.parse("SCHEM_SELECTOR_ITEM_REPLACE", player, filter.getName()));
}
SWItem item = new SWItem(m, name, Collections.singletonList(node.isDir() ? (Core.MESSAGE.parse("SCHEM_SELECTOR_DIR", player)) : Core.MESSAGE.parse("SCHEM_SELECTOR_ITEM_LORE_TYPE", player, node.getSchemtype().name())), !node.isDir() && !node.getSchemtype().writeable(), click -> {
});
if(!node.isDir() && node.getRank() > 0) {
if (!node.isDir() && node.getRank() > 0) {
item.setLore(Arrays.asList(Core.MESSAGE.parse("SCHEM_SELECTOR_ITEM_LORE_TYPE", player, node.getSchemtype().name()), Core.MESSAGE.parse("SCHEM_SELECTOR_RANK", player, node.getRank())));
}
return new SWListInv.SWListEntry<>(item, node);
@@ -251,7 +252,7 @@ public class SchematicSelector {
private void addLeftCloseAction(SWAnvilInv inv, Runnable runnable) {
AtomicBoolean wasLeft = new AtomicBoolean(false);
inv.addCloseCallback(() -> {
if(injectable.onAnvilInvCloseAction(this) == SchematicSelectorInjectable.AnvilInvCloseAction.REOPEN && !wasLeft.get()) {
if (injectable.onAnvilInvCloseAction(this) == SchematicSelectorInjectable.AnvilInvCloseAction.REOPEN && !wasLeft.get()) {
player.closeInventory();
Bukkit.getScheduler().runTaskLater(Core.getInstance(), runnable, 1);
}
@@ -266,9 +267,9 @@ public class SchematicSelector {
SWAnvilInv inv = new SWAnvilInv(player, Core.MESSAGE.parse("SCHEM_SELECTOR_CREATE_DIR_TITLE", player));
inv.setItem(Material.CHEST, Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_CLICK_BACK", player)), false);
inv.setCallback(s -> {
if(!SchematicNode.invalidSchemName(new String[] {s})) {
if(injectable.onFolderCreate(this, s)) {
SchematicNode.createSchematicDirectory(user.getId(), s, parent==null?0:parent.getId());
if (!SchematicNode.invalidSchemName(new String[]{s})) {
if (injectable.onFolderCreate(this, s)) {
SchematicNode.createSchematicDirectory(user.getId(), s, parent == null ? 0 : parent.getId());
openList(parent);
}
return;
@@ -286,7 +287,7 @@ public class SchematicSelector {
SWInventory inv = new SWInventory(player, 9 * 2, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TITLE", player));
InvCallback nameCallback = clickType -> {
if(clickType.isRightClick()) {
if (clickType.isRightClick()) {
filter = filter.withName(null);
openFilter();
} else {
@@ -300,21 +301,21 @@ public class SchematicSelector {
swAnvilInv.open();
}
};
if(filter.getName() == null) {
if (filter.getName() == null) {
inv.setItem(0, Material.NAME_TAG, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_NAME", player), nameCallback);
} else {
inv.setItem(0, Material.NAME_TAG, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_NAME", player), Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_NAME_SEARCH", player, filter.getName())), true, nameCallback);
}
InvCallback ownerCallback = clickType -> {
if(clickType.isRightClick()) {
if (clickType.isRightClick()) {
filter = filter.withOwner(null);
openFilter();
} else {
SWAnvilInv swAnvilInv = new SWAnvilInv(player, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_ENTER_OWNER", player));
swAnvilInv.setItem(SWItem.getMaterial("SKULL_ITEM"), (byte) 3, Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_CLICK_BACK", player)), false);
swAnvilInv.setItem(Material.SKELETON_SKULL, (byte) 3, Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_CLICK_BACK", player)), false);
swAnvilInv.setCallback(s -> {
if(SteamwarUser.get(s) != null) {
if (SteamwarUser.get(s) != null) {
filter = filter.withOwner(SteamwarUser.get(s).getId());
}
openFilter();
@@ -323,8 +324,8 @@ public class SchematicSelector {
swAnvilInv.open();
}
};
if(filter.getOwner() == null) {
inv.setItem(1, SWItem.getMaterial("SKULL_ITEM"), (byte) 3, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_OWNER", player), ownerCallback);
if (filter.getOwner() == null) {
inv.setItem(1, Material.SKELETON_SKULL, (byte) 3, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_OWNER", player), ownerCallback);
} else {
SteamwarUser tUser = SteamwarUser.byId(filter.getOwner());
SWItem item = SWItem.getPlayerSkull(tUser.getUserName());
@@ -335,14 +336,15 @@ public class SchematicSelector {
inv.setItem(1, item);
}
if(target.target != Target.SCHEMATIC_TYPE) {
if (target.target != Target.SCHEMATIC_TYPE) {
InvCallback schemTypeCallback = clickType -> {
if(clickType.isRightClick()) {
if (clickType.isRightClick()) {
filter = filter.withType(null);
openFilter();
} else {
List<SWListInv.SWListEntry<SchematicType>> types = new ArrayList<>();
SchematicType.values().forEach(schematicType -> types.add(new SWListInv.SWListEntry<>(new SWItem(SWItem.getMaterial(schematicType.getMaterial()), "§e" + schematicType.name(), Collections.emptyList(), schematicType.fightType(), n -> {}), schematicType)));
SchematicType.values().forEach(schematicType -> types.add(new SWListInv.SWListEntry<>(new SWItem(SWItem.getMaterial(schematicType.getMaterial()), "§e" + schematicType.name(), Collections.emptyList(), schematicType.fightType(), n -> {
}), schematicType)));
SWListInv<SchematicType> listInv = new SWListInv<>(player, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_SEL_TYPE", player), types, (clickType1, schematicType) -> {
filter = filter.withType(schematicType);
openFilter();
@@ -351,7 +353,7 @@ public class SchematicSelector {
}
};
if(filter.getType() == null) {
if (filter.getType() == null) {
inv.setItem(2, SWItem.getMaterial(SchematicType.Normal.getMaterial()), Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TYPE", player), schemTypeCallback);
} else {
inv.setItem(2, SWItem.getMaterial(filter.getType().getMaterial()), Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TYPE", player), Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TYPE_SEARCH", player, filter.getType().name())), true, schemTypeCallback);
@@ -359,7 +361,7 @@ public class SchematicSelector {
}
InvCallback materialCallback = clickType -> {
if(clickType.isRightClick()) {
if (clickType.isRightClick()) {
filter = filter.withItem(null);
openFilter();
} else {
@@ -370,9 +372,9 @@ public class SchematicSelector {
}
};
final int iSlot = target.target == Target.SCHEMATIC_TYPE?2:3;
final int iSlot = target.target == Target.SCHEMATIC_TYPE ? 2 : 3;
if(filter.getItem() == null) {
if (filter.getItem() == null) {
inv.setItem(iSlot, Material.STONE, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_MAT", player), materialCallback);
} else {
inv.setItem(iSlot, filter.getItem(), Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_MAT", player), Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_MAT_SEARCH", player, filter.getItem().name())), true, materialCallback);
@@ -397,7 +399,8 @@ public class SchematicSelector {
for (int i = 0; i < filters.length; i++) {
SelectorFilter filterCached = filters[i];
if (filterCached == null) {
inv.setItem(i + 9, new SWItem(Material.BARRIER, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_EMPTY", player), click -> {}));
inv.setItem(i + 9, new SWItem(Material.BARRIER, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_EMPTY", player), click -> {
}));
} else {
SWItem item = filterCached.toItemStack(player);
item.setEnchanted(filterCached.equals(filter));
@@ -408,19 +411,19 @@ public class SchematicSelector {
}
}
inv.setItem(7, SWItem.getDye(1), Core.MESSAGE.parse("SCHEM_SELECTOR_CANCEL", player), clickType -> {
inv.setItem(7, Material.RED_DYE, Core.MESSAGE.parse("SCHEM_SELECTOR_CANCEL", player), clickType -> {
filter = null;
depth = 0;
openList(null);
});
inv.setItem(8, SWItem.getDye(10), Core.MESSAGE.parse("SCHEM_SELECTOR_GO", player), clickType -> {
inv.setItem(8, Material.LIME_DYE, Core.MESSAGE.parse("SCHEM_SELECTOR_GO", player), clickType -> {
if (!filter.equals(filters[0])) {
for (int i = filters.length - 1; i > 0; i--) {
filters[i] = filters[i-1];
filters[i] = filters[i - 1];
if (filter.equals(filters[i])) {
filters[i] = null;
for (int j = i; j < filters.length - 1; j++) {
filters[j] = filters[j+1];
filters[j] = filters[j + 1];
}
filters[filters.length - 1] = null;
}
@@ -455,10 +458,10 @@ public class SchematicSelector {
injectable.onNodeFilter(this, node);
return !filter.matches(node);
});
if(target.target == Target.DIRECTORY) {
if (target.target == Target.DIRECTORY) {
nodes.removeIf(node -> !node.isDir());
}
if(target.target == Target.SCHEMATIC_TYPE) {
if (target.target == Target.SCHEMATIC_TYPE) {
nodes.removeIf(node -> node.isDir() || !node.getSchemtype().equals(target.type));
}
return nodes;
@@ -472,13 +475,13 @@ public class SchematicSelector {
nodes.removeIf(node -> !node.isDir());
break;
case SCHEMATIC_TYPE:
nodes.addAll(SchematicNode.accessibleByUserTypeParent(user, target.type, parent==null?null:parent.getId()));
nodes.addAll(SchematicNode.accessibleByUserTypeParent(user, target.type, parent == null ? null : parent.getId()));
break;
default:
nodes.addAll(SchematicNode.list(user, parent == null?null:parent.getId()));
nodes.addAll(SchematicNode.list(user, parent == null ? null : parent.getId()));
}
if(singleDirOpen && nodes.size() == 1 && nodes.get(0).isDir()) {
if (singleDirOpen && nodes.size() == 1 && nodes.get(0).isDir()) {
sdoTrigger = true;
}
return nodes;
@@ -553,22 +556,22 @@ public class SchematicSelector {
public boolean matches(SchematicNode node) {
boolean matches = name == null || node.getName().contains(name);
if(owner != null && node.getOwner() != owner) {
if (owner != null && node.getOwner() != owner) {
matches = false;
}
if(type != null && (node.isDir() || !node.getSchemtype().equals(type))) {
if (type != null && (node.isDir() || !node.getSchemtype().equals(type))) {
matches = false;
}
if(item != null) {
if (item != null) {
String i;
if(node.getItem().isEmpty()) {
i = node.isDir()?"CHEST":"CAULDRON";
if (node.getItem().isEmpty()) {
i = node.isDir() ? "CHEST" : "CAULDRON";
} else {
i = node.getItem();
}
if(!item.name().equals(i)) {
if (!item.name().equals(i)) {
matches = false;
}
}
@@ -581,16 +584,16 @@ public class SchematicSelector {
public List<String> getItemLore(Player player) {
List<String> lore = new ArrayList<>(4);
if(name != null) {
if (name != null) {
lore.add(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_NAME_SEARCH", player, name));
}
if(owner != null) {
if (owner != null) {
lore.add(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_OWNER_SEARCH", player, SteamwarUser.byId(owner).getUserName()));
}
if(type != null) {
if (type != null) {
lore.add(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TYPE_SEARCH", player, type.name()));
}
if(item != null) {
if (item != null) {
lore.add(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_MAT_SEARCH", player, item.name()));
}
@@ -604,18 +607,22 @@ public class SchematicSelector {
List<String> lore = getItemLore(player);
if (name != null) {
return new SWItem(Material.NAME_TAG, itemName, lore, false, click -> {});
return new SWItem(Material.NAME_TAG, itemName, lore, false, click -> {
});
} else if (owner != null) {
SWItem playerSkull = SWItem.getPlayerSkull(SteamwarUser.byId(owner).getUserName());
playerSkull.setName(itemName);
playerSkull.setLore(lore);
return playerSkull;
} else if (type != null) {
return new SWItem(SWItem.getMaterial(type.getMaterial()), itemName, lore, false, n -> {});
return new SWItem(SWItem.getMaterial(type.getMaterial()), itemName, lore, false, n -> {
});
} else if (item != null) {
return new SWItem(item, itemName, lore, false, click -> {});
return new SWItem(item, itemName, lore, false, click -> {
});
} else {
return new SWItem(Material.BARRIER, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TITLE_EMPTY", player), Collections.emptyList(), false, click -> {});
return new SWItem(Material.BARRIER, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TITLE_EMPTY", player), Collections.emptyList(), false, click -> {
});
}
}
@@ -623,8 +630,8 @@ public class SchematicSelector {
JsonObject object = new JsonObject();
object.addProperty("name", name);
object.addProperty("owner", owner);
object.addProperty("type", type == null?null:type.toDB());
object.addProperty("item", item == null?null:item.name());
object.addProperty("type", type == null ? null : type.toDB());
object.addProperty("item", item == null ? null : item.name());
return object;
}
@@ -640,13 +647,13 @@ public class SchematicSelector {
private enum Sorting {
NAME(Material.PAPER, "SCHEM_SELECTOR_SORTING_NAME", Comparator.comparing(SchematicNode::getName)),
TYPE(Material.CAULDRON, "SCHEM_SELECTOR_SORTING_TYPE", (o1, o2) -> {
if(o1.isDir() || o2.isDir()) {
if (o1.isDir() || o2.isDir()) {
return Boolean.compare(o1.isDir(), o2.isDir());
} else {
return o1.getSchemtype().name().compareTo(o2.getSchemtype().name());
}
}),
LAST_UPDATED(SWItem.getMaterial("WATCH"), "SCHEM_SELECTOR_SORTING_UPDATE", Comparator.comparing(SchematicNode::getLastUpdate));
LAST_UPDATED(Material.CLOCK, "SCHEM_SELECTOR_SORTING_UPDATE", Comparator.comparing(SchematicNode::getLastUpdate));
private final Material mat;
private final String name;
@@ -25,27 +25,38 @@ import org.bukkit.entity.Player;
public interface SchematicSelectorInjectable {
SchematicSelectorInjectable DEFAULT = new SchematicSelectorInjectable() {};
SchematicSelectorInjectable DEFAULT = new SchematicSelectorInjectable() {
};
default String createTitle(Player player) {
return Core.MESSAGE.parse("SCHEM_SELECTOR_TITLE", player);
}
default void onSelectorCreate(SchematicSelector selector) {}
default void onSelectorCreate(SchematicSelector selector) {
}
default void onListRender(SchematicSelector selector, SWListInv<SchematicNode> inv, SchematicNode parent) {}
default void onListRender(SchematicSelector selector, SWListInv<SchematicNode> inv, SchematicNode parent) {
}
default void onFilterRender(SchematicSelector selector, SWInventory inventory) {}
default void onFilterRender(SchematicSelector selector, SWInventory inventory) {
}
default void onFilterApply(SchematicSelector selector) {}
default void onFilterApply(SchematicSelector selector) {
}
default boolean onFolderCreate(SchematicSelector selector, String name) {return true;}
default boolean onFolderCreate(SchematicSelector selector, String name) {
return true;
}
default void onNodeFilter(SchematicSelector selector, SchematicNode node) {}
default void onNodeFilter(SchematicSelector selector, SchematicNode node) {
}
default void onSelectorOpen(SchematicSelector selector, OpenFrom from) {}
default void onSelectorOpen(SchematicSelector selector, OpenFrom from) {
}
default AnvilInvCloseAction onAnvilInvCloseAction(SchematicSelector selector) {return AnvilInvCloseAction.CLOSE;}
default AnvilInvCloseAction onAnvilInvCloseAction(SchematicSelector selector) {
return AnvilInvCloseAction.CLOSE;
}
enum OpenFrom {
FRESH,
@@ -20,8 +20,6 @@
package de.steamwar.inventory;
import de.steamwar.core.Core;
import de.steamwar.inventory.SWItem;
import de.steamwar.inventory.SWListInv;
import lombok.experimental.UtilityClass;
import org.bukkit.Material;
import org.bukkit.entity.Player;
@@ -39,11 +37,10 @@ public class UtilGui {
public static void openMaterialSelector(Player player, String title, Consumer<Material> callback) {
List<SWListInv.SWListEntry<Material>> materials = new LinkedList<>();
for(Material material : Material.values()){
if(material.name().startsWith(Material.LEGACY_PREFIX) || !material.isItem())
continue;
for (Material material : Material.values()) {
if (material.name().startsWith(Material.LEGACY_PREFIX) || !material.isItem()) continue;
SWItem item = new SWItem(material, "§7" + material.name());
if(item.getItemMeta() != null && material.isItem()) {
if (item.getItemMeta() != null && material.isItem()) {
materials.add(new SWListInv.SWListEntry<>(item, material));
}
}
@@ -20,7 +20,6 @@
package de.steamwar.linkage;
import de.steamwar.command.SWCommand;
import de.steamwar.core.Core;
import de.steamwar.message.Message;
import de.steamwar.network.packets.PacketHandler;
import lombok.NonNull;
@@ -38,17 +37,6 @@ public class SpigotLinker extends AbstractLinker<JavaPlugin> {
this.message = message;
}
@Override
protected boolean versionCheck(@NonNull Class<?> clazz, MinVersion minVersion, MaxVersion maxVersion) {
if (minVersion != null && Core.getVersion() < minVersion.value()) {
return false;
}
if (maxVersion != null && Core.getVersion() > maxVersion.value()) {
return false;
}
return true;
}
@Override
protected boolean pluginCheck(@NonNull Class<?> clazz, PluginCheck pluginCheck) {
if (pluginCheck.has() == PluginCheck.Has.THIS && Bukkit.getPluginManager().getPlugin(pluginCheck.value()) != null) {
@@ -20,7 +20,6 @@
package de.steamwar.message;
import de.steamwar.core.BountifulWrapper;
import de.steamwar.core.Core;
import de.steamwar.sql.SteamwarUser;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.ClickEvent;
@@ -30,7 +29,6 @@ import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;
@@ -39,36 +37,36 @@ public class Message {
private final String resourceBundleName;
private final ClassLoader classLoader;
public Message(String resourceBundleName, ClassLoader classLoader){
public Message(String resourceBundleName, ClassLoader classLoader) {
this.resourceBundleName = resourceBundleName;
this.classLoader = classLoader;
}
/* Parsing input to a message */
public TextComponent parseToComponent(String message, boolean prefixed, CommandSender sender, Object... params){
public TextComponent parseToComponent(String message, boolean prefixed, CommandSender sender, Object... params) {
return new TextComponent(TextComponent.fromLegacyText(parse(message, prefixed, sender, params)));
}
public String parsePrefixed(String message, CommandSender sender, Object... params){
public String parsePrefixed(String message, CommandSender sender, Object... params) {
return parse(message, true, sender, params);
}
public String parse(String message, CommandSender sender, Object... params){
public String parse(String message, CommandSender sender, Object... params) {
return parse(message, false, sender, params);
}
private String parse(String message, boolean prefixed, CommandSender sender, Object... params){
private String parse(String message, boolean prefixed, CommandSender sender, Object... params) {
Locale locale;
if(sender instanceof Player)
if (sender instanceof Player) {
locale = getLocale((Player) sender);
else
} else {
locale = Locale.getDefault();
}
ResourceBundle resourceBundle = ResourceBundle.getBundle(resourceBundleName, locale, classLoader);
String pattern = "";
if(prefixed)
pattern = fromRB(resourceBundle, "PREFIX") + " ";
if (prefixed) pattern = fromRB(resourceBundle, "PREFIX") + " ";
pattern += fromRB(resourceBundle, message);
for (int i = 0; i < params.length; i++) {
@@ -82,84 +80,90 @@ public class Message {
}
private String fromRB(ResourceBundle bundle, String key) {
String result = bundle.getString(key);
if(Core.getVersion() < 12)
result = new String(result.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
return result;
return bundle.getString(key);
}
private Locale getLocale(Player player){
private Locale getLocale(Player player) {
return SteamwarUser.get(player.getUniqueId()).getLocale();
}
/* Send a message to one player */
public void send(String message, CommandSender sender, Object... params){
public void send(String message, CommandSender sender, Object... params) {
send(message, true, sender, ChatMessageType.SYSTEM, null, null, params);
}
public void sendPrefixless(String message, CommandSender sender, Object... params){
public void sendPrefixless(String message, CommandSender sender, Object... params) {
send(message, false, sender, ChatMessageType.SYSTEM, null, null, params);
}
public void send(String message, CommandSender sender, ChatMessageType type, Object... params){
public void send(String message, CommandSender sender, ChatMessageType type, Object... params) {
send(message, true, sender, type, null, null, params);
}
public void sendPrefixless(String message, CommandSender sender, ChatMessageType type, Object... params){
public void sendPrefixless(String message, CommandSender sender, ChatMessageType type, Object... params) {
send(message, false, sender, type, null, null, params);
}
public void send(String message, CommandSender sender, String onHover, ClickEvent onClick, Object... params){
public void send(String message, CommandSender sender, String onHover, ClickEvent onClick, Object... params) {
send(message, true, sender, ChatMessageType.SYSTEM, onHover, onClick, params);
}
public void sendPrefixless(String message, CommandSender sender, String onHover, ClickEvent onClick, Object... params){
public void sendPrefixless(String message, CommandSender sender, String onHover, ClickEvent onClick, Object... params) {
send(message, false, sender, ChatMessageType.SYSTEM, onHover, onClick, params);
}
public void send(String message, boolean prefixed, CommandSender sender, ChatMessageType type, String onHover, ClickEvent onClick, Object... params){
public void send(String message, boolean prefixed, CommandSender sender, ChatMessageType type, String onHover, ClickEvent onClick, Object... params) {
TextComponent msg = parseToComponent(message, prefixed, sender, params);
if(onHover != null)
if (onHover != null) {
msg.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(onHover)));
if(onClick != null)
}
if (onClick != null) {
msg.setClickEvent(onClick);
}
if(sender instanceof Player)
BountifulWrapper.impl.sendMessage((Player)sender, type, msg);
else
if (sender instanceof Player) {
BountifulWrapper.impl.sendMessage((Player) sender, type, msg);
} else {
sender.sendMessage(msg.toPlainText());
}
}
/* Send message to all players */
public void broadcastPrefixless(String message, String onHover, ClickEvent onClick, Object... params){
for(Player player : Bukkit.getOnlinePlayers())
public void broadcastPrefixless(String message, String onHover, ClickEvent onClick, Object... params) {
for (Player player : Bukkit.getOnlinePlayers()) {
sendPrefixless(message, player, parse(onHover, false, player), onClick, params);
}
}
public void broadcastPrefixless(String message, Object... params){
for(Player player : Bukkit.getOnlinePlayers())
public void broadcastPrefixless(String message, Object... params) {
for (Player player : Bukkit.getOnlinePlayers()) {
sendPrefixless(message, player, ChatMessageType.SYSTEM, params);
}
}
public void broadcastActionbar(String message, Object... params){
for(Player player : Bukkit.getOnlinePlayers())
public void broadcastActionbar(String message, Object... params) {
for (Player player : Bukkit.getOnlinePlayers()) {
send(message, player, ChatMessageType.ACTION_BAR, params);
}
}
public void broadcast(String message, String onHover, ClickEvent onClick, Object... params){
for(Player player : Bukkit.getOnlinePlayers())
public void broadcast(String message, String onHover, ClickEvent onClick, Object... params) {
for (Player player : Bukkit.getOnlinePlayers()) {
send(message, player, parse(onHover, false, player), onClick, params);
}
}
public void broadcast(String message, Object... params){
for(Player player : Bukkit.getOnlinePlayers())
public void broadcast(String message, Object... params) {
for (Player player : Bukkit.getOnlinePlayers()) {
send(message, player, ChatMessageType.SYSTEM, params);
}
}
public void chat(String message, Object... params){
for(Player player : Bukkit.getOnlinePlayers())
public void chat(String message, Object... params) {
for (Player player : Bukkit.getOnlinePlayers()) {
sendPrefixless(message, player, ChatMessageType.CHAT, params);
}
}
}
@@ -67,7 +67,7 @@ public class CoreNetworkHandler extends PacketHandler {
@Handler
public void handlePingPacket(PingPacket packet) {
UUID uuid = SteamwarUser.byId(packet.getId()).getUUID();
if(Bukkit.getPlayer(uuid) != null) {
if (Bukkit.getPlayer(uuid) != null) {
Player player = Bukkit.getPlayer(uuid);
BountifulWrapper.impl.playPling(player);
}
@@ -51,8 +51,9 @@ public class InventoryHandler extends PacketHandler {
SWInventory inventory = new SWInventory(player, packet.getSize(), packet.getTitle(), items);
inventory.addCloseCallback(click -> {
if(player.getOpenInventory().getType() != InventoryType.CHEST)
if (player.getOpenInventory().getType() != InventoryType.CHEST) {
NetworkSender.send(InventoryCallbackPacket.builder().owner(packet.getPlayer()).type(InventoryCallbackPacket.CallbackType.CLOSE).build(), player);
}
});
inventory.open();
}
@@ -19,17 +19,16 @@
package de.steamwar.network.handlers;
import de.steamwar.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import net.minecraft.network.protocol.game.ClientboundServerDataPacket;
import net.minecraft.network.protocol.game.ServerboundChatSessionUpdatePacket;
@Linked
@MinVersion(19)
public class ServerDataHandler {
public ServerDataHandler() {
TinyProtocol.instance.addFilter(Reflection.getClass("net.minecraft.network.protocol.game.ClientboundServerDataPacket"), (p, o) -> null);
TinyProtocol.instance.addFilter(Reflection.getClass("net.minecraft.network.protocol.game.ServerboundChatSessionUpdatePacket"), (player, packet) -> null);
TinyProtocol.instance.addFilter(ClientboundServerDataPacket.class, (p, o) -> null);
TinyProtocol.instance.addFilter(ServerboundChatSessionUpdatePacket.class, (player, packet) -> null);
}
}
@@ -28,7 +28,8 @@ public class BauServerInfo {
static {
try {
bauOwner = Integer.parseInt(Bukkit.getWorlds().get(0).getName());
} catch (NumberFormatException ignored) {}
} catch (NumberFormatException ignored) {
}
}
public static Integer getOwnerId() {
@@ -20,12 +20,54 @@
package de.steamwar.scoreboard;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Scoreboard;
public interface SWScoreboard {
public static final SWScoreboard impl = VersionDependent.getVersionImpl(Core.getInstance());
import java.util.HashMap;
import java.util.Map;
boolean createScoreboard(Player player, ScoreboardCallback callback);
void removeScoreboard(Player player);
public class SWScoreboard {
public static final SWScoreboard impl = new SWScoreboard();
private static final HashMap<Player, ScoreboardCallback> playerBoards = new HashMap<>();
private static final String SIDEBAR = "sw-sidebar";
static {
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
for (Map.Entry<Player, ScoreboardCallback> scoreboard : playerBoards.entrySet()) {
render(scoreboard.getKey(), scoreboard.getValue());
}
}, 10, 5);
}
private static void render(Player player, ScoreboardCallback callback) {
if (player.getScoreboard().getObjective(SIDEBAR) != null) {
player.getScoreboard().getObjective(SIDEBAR).unregister();
}
Objective objective = player.getScoreboard().registerNewObjective(SIDEBAR, "dummy", Component.text(callback.getTitle()));
objective.setAutoUpdateDisplay(true);
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
callback.getData().forEach((text, score) -> objective.getScore(text).setScore(score));
}
public boolean createScoreboard(Player player, ScoreboardCallback callback) {
Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
player.setScoreboard(scoreboard);
render(player, callback);
playerBoards.put(player, callback);
return true;
}
public void removeScoreboard(Player player) {
player.getScoreboard().getObjective(SIDEBAR).unregister();
playerBoards.remove(player);
}
}
@@ -48,12 +48,12 @@ public class PersonalKit {
return kit.getRawArmor();
}
public ItemStack[] getInventory(){
public ItemStack[] getInventory() {
YamlConfiguration config = YamlConfiguration.loadConfiguration(new StringReader(getRawInventory()));
return Objects.requireNonNull(config.getList("Inventory")).toArray(new ItemStack[0]);
}
public ItemStack[] getArmor(){
public ItemStack[] getArmor() {
YamlConfiguration config = YamlConfiguration.loadConfiguration(new StringReader(getRawArmor()));
return Objects.requireNonNull(config.getList("Armor")).toArray(new ItemStack[4]);
}
@@ -79,26 +79,24 @@ public class PersonalKit {
kit.delete();
}
public static List<PersonalKit> get(int userID, String gamemode){
public static List<PersonalKit> get(int userID, String gamemode) {
return InternalKit.get(userID, gamemode).stream().map(PersonalKit::new).collect(Collectors.toList());
}
public static PersonalKit get(int userID, String gamemode, String name) {
InternalKit kit = InternalKit.get(userID, gamemode, name);
if(kit == null)
return null;
if (kit == null) return null;
return new PersonalKit(kit);
}
public static PersonalKit create(int userID, String gamemode, String name, ItemStack[] inventory, ItemStack[] armor){
public static PersonalKit create(int userID, String gamemode, String name, ItemStack[] inventory, ItemStack[] armor) {
InternalKit kit = InternalKit.create(userID, gamemode, name, saveInvConfig("Inventory", inventory), saveInvConfig("Armor", armor));
return new PersonalKit(kit);
}
public static PersonalKit getKitInUse(int userID, String gamemode) {
InternalKit kit = InternalKit.getKitInUse(userID, gamemode);
if(kit == null)
return null;
if (kit == null) return null;
return new PersonalKit(kit);
}
@@ -28,7 +28,6 @@ import org.bukkit.entity.Player;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -46,9 +45,6 @@ public class SQLWrapperImpl implements SQLWrapper<Material> {
@Override
public List<Material> getMaterialWithGreaterBlastResistance(double maxBlastResistance) {
if (Core.getVersion() <= 12) {
return Collections.emptyList();
}
return Arrays.stream(Material.values())
.filter(material -> !material.isLegacy())
.filter(Material::isBlock)
@@ -61,11 +57,13 @@ public class SQLWrapperImpl implements SQLWrapper<Material> {
@Override
public void additionalExceptionMetadata(StringBuilder builder) {
builder.append("\nPlayers: ");
for (Player player : Bukkit.getOnlinePlayers())
for (Player player : Bukkit.getOnlinePlayers()) {
builder.append(player.getName()).append(" ");
}
builder.append("\nWorlds: ");
for (World world : Bukkit.getWorlds())
for (World world : Bukkit.getWorlds()) {
builder.append(world.getName()).append(" ");
}
builder.append("\nServer: ").append(SERVER_VERSION);
}
}
@@ -25,7 +25,6 @@ import org.bukkit.entity.Player;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
public class SchematicData {
@@ -33,13 +32,11 @@ public class SchematicData {
public SchematicData(SchematicNode node) {
this.data = NodeData.getLatest(node);
if(node.isDir())
throw new SecurityException("Node is Directory");
if (node.isDir()) throw new SecurityException("Node is Directory");
}
public SchematicData(SchematicNode node, int revision) {
if(node.isDir())
throw new SecurityException("Node is Directory");
if (node.isDir()) throw new SecurityException("Node is Directory");
if (revision < 1) {
this.data = NodeData.getLatest(node);
@@ -20,18 +20,49 @@
package de.steamwar.techhider;
import de.steamwar.Reflection;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
import net.minecraft.core.IdMapper;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import org.bukkit.Material;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import java.util.HashSet;
import java.util.Set;
public interface BlockIds {
BlockIds impl = VersionDependent.getVersionImpl(Core.getInstance());
public class BlockIds {
public static final BlockIds impl = new BlockIds();
Reflection.Method getCombinedId = Reflection.getTypedMethod(TechHider.block, null, int.class, TechHider.iBlockData);
public int materialToId(Material material) {
return getCombinedId(getBlock(material).defaultBlockState());
}
int getCombinedId(Object iBlockData);
int materialToId(Material material);
Set<Integer> materialToAllIds(Material material);
private static final FluidState water = Fluids.WATER.getSource(false);
private static final Iterable<BlockState> registryBlockId = (Iterable<BlockState>) Reflection.getField(TechHider.block, IdMapper.class, 0).get(null);
public Set<Integer> materialToAllIds(Material material) {
Set<Integer> ids = new HashSet<>();
for (BlockState data : getBlock(material).getStateDefinition().getPossibleStates()) {
ids.add(getCombinedId(data));
}
if (material == Material.WATER) {
for (BlockState data : registryBlockId) {
if (data.getFluidState() == water) {
ids.add(getCombinedId(data));
}
}
}
return ids;
}
private Block getBlock(Material material) {
return CraftMagicNumbers.getBlock(material);
}
public int getCombinedId(BlockState blockData) {
return Block.getId(blockData);
}
}
@@ -19,50 +19,64 @@
package de.steamwar.techhider;
import de.steamwar.Reflection;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.Getter;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Material;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.SimpleBitStorage;
import net.minecraft.world.level.block.entity.BlockEntityType;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.Set;
import de.steamwar.Reflection;
import io.netty.buffer.Unpooled;
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.util.SimpleBitStorage;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
public class ChunkHider {
public static final ChunkHider impl = new ChunkHider();
public Class<?> mapChunkPacket() {
return ClientboundLevelChunkWithLightPacket.class;
}
private static final UnaryOperator<Object> chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class);
private static final UnaryOperator<Object> chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class);
private static final Reflection.Field<Integer> chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0);
private static final Reflection.Field<Integer> chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1);
private static final Reflection.Field<ClientboundLevelChunkPacketData> chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0);
private static final Reflection.Field<byte[]> dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0);
private static final Reflection.Field<List> tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0);
public ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPacket(Player recivingPlayer, Set<String> hiddenBlockEntities , QuadFunction<Player, Integer, Integer, Integer, Boolean> isPlayerPrivilegedToAccessBlockPos, BlockState blockToObfuscateTo, Set<Integer> blockIdsToObfuscate, ClientboundLevelChunkWithLightPacket packet) {
int chunkX = packet.getX();
int chunkZ = packet.getZ();
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider techHider) {
return (p, packet) -> {
int chunkX = chunkXField.get(packet);
int chunkZ = chunkZField.get(packet);
if (techHider.getLocationEvaluator().skipChunk(p, chunkX, chunkZ)) {
return packet;
}
Object clonedPacket = chunkPacketCloner.apply(packet);
Object dataWrapper = chunkDataCloner.apply(chunkData.get(clonedPacket));
packet = chunkPacketCloner.apply(packet);
Object dataWrapper = chunkDataCloner.apply(chunkData.get(packet));
tileEntities.set(dataWrapper, ((List<?>)tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList()));
Set<String> hiddenBlockEntities = techHider.getHiddenBlockEntities();
tileEntities.set(dataWrapper, ((List<?>) tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList()));
ByteBuf in = Unpooled.wrappedBuffer(dataField.get(dataWrapper));
ByteBuf out = Unpooled.buffer(in.readableBytes() + 64);
for(int yOffset = recivingPlayer.getWorld().getMinHeight(); yOffset < recivingPlayer.getWorld().getMaxHeight(); yOffset += 16) {
SectionHider section = new SectionHider(recivingPlayer, blockToObfuscateTo, blockIdsToObfuscate, in, out, chunkX, yOffset/16, chunkZ);
for (int yOffset = p.getWorld().getMinHeight(); yOffset < p.getWorld().getMaxHeight(); yOffset += 16) {
SectionHider section = new SectionHider(p, techHider, in, out, chunkX, yOffset / 16, chunkZ);
section.copyBlockCount();
blocks(section, isPlayerPrivilegedToAccessBlockPos);
blocks(section);
biomes(section);
}
@@ -76,43 +90,60 @@ public class ChunkHider {
chunkData.set(packet, dataWrapper);
return packet;
};
}
private static final Registry<BlockEntityType<?>> registry = Reflection.getField(BuiltInRegistries.class, "BLOCK_ENTITY_TYPE", Registry.class).get(null);
private static final Reflection.Method getKey = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "getKey", ResourceLocation.class, Object.class);
public static final Class<?> tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo");
protected static final Reflection.Field<BlockEntityType> entityType = Reflection.getField(tileEntity, BlockEntityType.class, 0);
private static final Class<?> builtInRegestries = Reflection.getClass("net.minecraft.core.registries.BuiltInRegistries");
private static final Class<?> registry = Reflection.getClass("net.minecraft.core.Registry");
private static final Reflection.Field<?> nameField = Reflection.getField(builtInRegestries, "BLOCK_ENTITY_TYPE", registry);
private static final Class<?> resourceLocation = Reflection.getClass("net.minecraft.resources.ResourceLocation");
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);
protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) {
return !hiddenBlockEntities.contains(getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile))));
BlockEntityType type = entityType.get(tile);
String path = ((ResourceLocation) getKey.invoke(registry, type)).getPath();
return !hiddenBlockEntities.contains(path);
}
private void blocks(SectionHider section, QuadFunction<Player, Integer, Integer, Integer, Boolean> isPlayerPrivilegedToAccessBlockPos) {
private void blocks(SectionHider section) {
section.copyBitsPerBlock();
boolean singleValued = section.getBitsPerBlock() == 0;
if (singleValued) {
int value = ProtocolUtils.readVarInt(section.getIn());
ProtocolUtils.writeVarInt(section.getOut(), section.getBlockIdsToObfuscate().contains(value) ? section.getTarget() : value);
ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getObfuscate().contains(value) ? section.getTarget() : value);
return;
} else if (section.getBitsPerBlock() < 9) {
// Indirect (paletted) storage only present when bitsPerBlock < 9 in 1.21+
section.processPalette();
}
if (section.isSkipSection() || (!section.blockPrecise() && section.isPaletted())) {
section.skipNewDataArray(4096);
return;
}
SimpleBitStorage values = new SimpleBitStorage(section.getBitsPerBlock(), 4096, section.readNewDataArray(4096));
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int pos = (((y * 16) + z) * 16) + x;
int blockId = values.get(pos);
if(!isPlayerPrivilegedToAccessBlockPos.apply(section.player, section.getOffsetX() * x, section.getOffsetY() * y, section.getOffsetZ() * z) && !section.getBlockIdsToObfuscate().contains(blockId)) {
values.set(pos, section.getTarget());
TechHider.State test = section.test(x, y, z);
switch (test) {
case SKIP:
break;
case CHECK:
if (!section.getObfuscate().contains(values.get(pos))) {
break;
}
case HIDE:
values.set(pos, section.getTarget());
break;
case HIDE_AIR:
default:
values.set(pos, section.getAir());
}
}
}
@@ -123,9 +154,9 @@ public class ChunkHider {
private void biomes(SectionHider section) {
section.copyBitsPerBlock();
if(section.getBitsPerBlock() == 0) {
if (section.getBitsPerBlock() == 0) {
section.copyVarInt();
} else if(section.getBitsPerBlock() < 6) {
} else if (section.getBitsPerBlock() < 6) {
section.skipPalette();
section.skipNewDataArray(64);
} else {
@@ -163,16 +194,24 @@ public class ChunkHider {
this.chunkX = chunkX;
this.chunkY = chunkY;
this.chunkZ = chunkZ;
this.offsetX = 16*chunkX;
this.offsetY = 16*chunkY;
this.offsetZ = 16*chunkZ;
this.blockIdsToObfuscate = blockIdsToObfuscate;
this.blockIdToObfuscateTo = BlockIds.impl.getCombinedId(blockToObfuscateTo);
this.offsetX = 16 * chunkX;
this.offsetY = 16 * chunkY;
this.offsetZ = 16 * chunkZ;
this.skipSection = techHider.getLocationEvaluator().skipChunkSection(player, chunkX, chunkY, chunkZ);
this.paletted = false;
this.bitsPerBlock = 0;
this.air = TechHider.AIR_ID;
this.target = techHider.getObfuscationTargetId();
this.obfuscate = techHider.getObfuscateIds();
}
public boolean blockPrecise() {
return techHider.getLocationEvaluator().blockPrecise(player, chunkX, chunkY, chunkZ);
}
public TechHider.State test(int x, int y, int z) {
return techHider.getLocationEvaluator().check(player, offsetX + x, offsetY + y, offsetZ + z);
}
public void copyBlockCount() {
@@ -193,28 +232,35 @@ public class ChunkHider {
public void skipPalette() {
int paletteLength = copyVarInt();
for(int i = 0; i < paletteLength; i++)
for (int i = 0; i < paletteLength; i++) {
copyVarInt();
}
}
public void processPalette() {
int paletteLength = copyVarInt();
if(paletteLength == 0)
if (skipSection) {
skipPalette();
return;
}
int paletteLength = copyVarInt();
if (paletteLength == 0) return;
paletted = true;
air = 0;
target = 0;
for(int i = 0; i < paletteLength; i++) {
for (int i = 0; i < paletteLength; i++) {
int entry = ProtocolUtils.readVarInt(in);
if(blockIdsToObfuscate.contains(entry))
entry = blockIdToObfuscateTo;
if (obfuscate.contains(entry)) {
entry = techHider.getObfuscationTargetId();
}
if(entry == BlockIds.impl.materialToId(Material.AIR))
if (entry == TechHider.AIR_ID) {
air = i;
else if(entry == blockIdToObfuscateTo)
} else if (entry == techHider.getObfuscationTargetId()) {
target = i;
}
ProtocolUtils.writeVarInt(out, entry);
}
@@ -223,7 +269,7 @@ public class ChunkHider {
public void skipDataArray() {
int dataArrayLength = copyVarInt();
out.writeBytes(in, dataArrayLength*8);
out.writeBytes(in, dataArrayLength * 8);
}
public void skipNewDataArray(int entries) {
@@ -231,15 +277,16 @@ public class ChunkHider {
return;
}
char valuesPerLong = (char)(64 / bitsPerBlock);
char valuesPerLong = (char) (64 / bitsPerBlock);
int i1 = (entries + valuesPerLong - 1) / valuesPerLong;
out.writeBytes(in, i1 * Long.BYTES);
}
public long[] readDataArray() {
long[] array = new long[copyVarInt()];
for(int i = 0; i < array.length; i++)
for (int i = 0; i < array.length; i++) {
array[i] = in.readLong();
}
return array;
}
@@ -252,15 +299,17 @@ public class ChunkHider {
char valuesPerLong = (char) (64 / bitsPerBlock);
int i1 = (entries + valuesPerLong - 1) / valuesPerLong;
long[] array = new long[i1];
for(int i = 0; i < i1; i++)
for (int i = 0; i < i1; i++) {
array[i] = in.readLong();
}
return array;
}
public void writeDataArray(long[] array) {
for(long l : array)
for (long l : array) {
out.writeLong(l);
}
}
}
}
@@ -19,8 +19,8 @@
package de.steamwar.techhider;
import de.steamwar.Reflection;
import com.google.common.primitives.Bytes;
import de.steamwar.Reflection;
import io.netty.buffer.ByteBuf;
import java.lang.reflect.Array;
@@ -33,7 +33,8 @@ import java.util.function.BiFunction;
import java.util.function.UnaryOperator;
public class ProtocolUtils {
private ProtocolUtils() {}
private ProtocolUtils() {
}
@Deprecated
public static BiFunction<Object, UnaryOperator<Object>, Object> arrayCloneGenerator(Class<?> elementClass) {
@@ -41,8 +42,9 @@ public class ProtocolUtils {
int length = Array.getLength(array);
Object result = Array.newInstance(elementClass, length);
for(int i = 0; i < length; i++)
for (int i = 0; i < length; i++) {
Array.set(result, i, worker.apply(Array.get(array, i)));
}
return result;
};
@@ -69,16 +71,17 @@ public class ProtocolUtils {
}
private static BiConsumer<Object, Object> shallowFill(Class<?> clazz) {
if(clazz == null)
return (source, clone) -> {};
if (clazz == null) {
return (source, clone) -> {
};
}
BiConsumer<Object, Object> superFiller = shallowFill(clazz.getSuperclass());
Field[] fds = clazz.getDeclaredFields();
List<Field> fields = new ArrayList<>();
for(Field field : fds) {
if (Modifier.isStatic(field.getModifiers()))
continue;
for (Field field : fds) {
if (Modifier.isStatic(field.getModifiers())) continue;
field.setAccessible(true);
fields.add(field);
@@ -87,7 +90,7 @@ public class ProtocolUtils {
return (source, clone) -> {
superFiller.accept(source, clone);
try {
for(Field field : fields) {
for (Field field : fields) {
field.set(clone, field.get(source));
}
} catch (IllegalAccessException e) {
@@ -97,10 +100,9 @@ public class ProtocolUtils {
};
}
public static int posToChunk(int c){
public static int posToChunk(int c) {
int chunk = c / 16;
if(c < 0)
chunk--;
if (c < 0) chunk--;
return chunk;
}
@@ -132,8 +134,7 @@ public class ProtocolUtils {
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
if (++numRead > 5)
throw new SecurityException("VarInt too long");
if (++numRead > 5) throw new SecurityException("VarInt too long");
} while ((read & 0b10000000) != 0);
return result;
@@ -170,7 +171,7 @@ public class ProtocolUtils {
public static byte[] writeVarInt(int value) {
List<Byte> buffer = new ArrayList<>(5);
do {
byte temp = (byte)(value & 0b01111111);
byte temp = (byte) (value & 0b01111111);
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
value >>>= 7;
if (value != 0) {
@@ -182,20 +183,20 @@ public class ProtocolUtils {
}
@Deprecated
public static class ChunkPos{
public static class ChunkPos {
final int x;
final int z;
public ChunkPos(int x, int z){
public ChunkPos(int x, int z) {
this.x = x;
this.z = z;
}
public final int x(){
public final int x() {
return x;
}
public final int z(){
public final int z() {
return z;
}
}
@@ -19,19 +19,77 @@
package de.steamwar.techhider;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
import de.steamwar.Reflection;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.SignBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.function.BiFunction;
public interface ProtocolWrapper {
ProtocolWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public class ProtocolWrapper {
public static final ProtocolWrapper impl = new 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);
boolean unfilteredTileEntityDataAction(Object packet);
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider) {
return (p, packet) -> {
TechHider.LocationEvaluator locationEvaluator = techHider.getLocationEvaluator();
Object chunkCoords = multiBlockChangeChunk.get(packet);
int chunkX = TechHider.blockPositionX.get(chunkCoords);
int chunkY = TechHider.blockPositionY.get(chunkCoords);
int chunkZ = TechHider.blockPositionZ.get(chunkCoords);
if (locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ)) {
return packet;
}
BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider);
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 + (pos >> 8 & 0xF), 16 * chunkY + (pos & 0xF), 16 * chunkZ + (pos >> 4 & 0xF))) {
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;
}
}
BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider);
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);
private static final BlockEntityType<?> signType = Reflection.getField(BlockEntityType.class, BlockEntityType.class, 0, SignBlockEntity.class).get(null);
public boolean unfilteredTileEntityDataAction(Object packet) {
return tileEntityType.get(packet) != signType;
}
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) {
return null;
}
}