Skip to content

Commit 6365c9a

Browse files
committed
Update 4 ready
1 parent 43dedfd commit 6365c9a

6 files changed

Lines changed: 224 additions & 373 deletions

File tree

build.gradle

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ configurations {
2121
canBeConsumed = false
2222
extendsFrom(vineImplementation, vineCompileOnly)
2323
}
24+
vineServerJar {
25+
canBeResolved = true
26+
canBeConsumed = false
27+
transitive = false
28+
}
2429
implementation.extendsFrom(vineImplementation)
2530
compileOnly.extendsFrom(vineCompileOnly)
2631
}
@@ -35,6 +40,10 @@ java {
3540
toolchain.languageVersion = JavaLanguageVersion.of(java_version)
3641
}
3742

43+
hytale {
44+
updateChannel = "pre-release"
45+
}
46+
3847
repositories {
3948
mavenCentral()
4049
maven {
@@ -78,9 +87,13 @@ repositories {
7887
}
7988
}
8089

90+
def hytaleServerDep = 'com.hypixel.hytale:Server:2026.03.20-db226053c'
91+
8192
dependencies {
93+
vineServerJar hytaleServerDep
94+
vineCompileOnly hytaleServerDep
8295
vineflowerTool 'org.vineflower:vineflower:1.11.2'
83-
implementation 'com.azuredoom.levelingcore:LevelingCore:0.+'
96+
implementation 'com.azuredoom.levelingcore:LevelingCore:1.0.0-alpha2'
8497
vineImplementation 'curse.maven:hyui-1431415:7693755'
8598
vineImplementation 'curse.maven:dynamictooltipslib-1459711:7702598'
8699
vineCompileOnly 'at.helpch:placeholderapi-hytale:1.0.7'
@@ -188,76 +201,77 @@ publishing {
188201

189202
tasks.register("decompileLibsWithVineflower") {
190203
group = "decompilation"
191-
description = "Decompile /libs jars + implementation/compileOnly deps (compileClasspath) into build/vineflower/<jarName>/"
204+
description = "Decompile only com/hytale from the Hytale Server jar into build/vineflower/hytale-server/"
192205

193-
def libsDir = file("$rootDir/libs")
194-
def outDir = file("$buildDir/vineflower")
206+
def outDir = file("$buildDir/vineflower/hytale-server")
207+
def tempDir = file("$buildDir/tmp/vineflower")
208+
def serverConfig = configurations.vineServerJar
209+
def classpathConfig = configurations.vineDecompileClasspath
195210

196-
def depConfig = configurations.vineDecompileClasspath
197-
198-
inputs.files(depConfig)
199-
inputs.dir(libsDir)
211+
inputs.files(serverConfig, classpathConfig)
200212
outputs.dir(outDir)
201213

202214
doLast {
203215
outDir.mkdirs()
216+
tempDir.mkdirs()
204217

205218
def vineflowerJar = configurations.vineflowerTool.singleFile
206219

207220
def javaExe = javaToolchains.launcherFor {
208221
languageVersion = JavaLanguageVersion.of(java_version)
209222
}.get().executablePath.asFile.absolutePath
210223

211-
212-
def localJars = libsDir.exists()
213-
? fileTree(dir: libsDir, include: ["*.jar"]).files
214-
: [] as Set<File>
215-
216-
def depJars = depConfig.incoming.artifactView { it.lenient(true) }
217-
.files.files
218-
.findAll { it.name.endsWith(".jar") } as Set<File>
219-
220-
def jars = (localJars + depJars).toList().unique()
221-
222-
if (jars.isEmpty()) {
223-
logger.lifecycle("No .jar files found in /libs or on compileClasspath.")
224-
return
224+
def serverJar = serverConfig.singleFile
225+
if (!serverJar.exists()) {
226+
throw new GradleException("Could not resolve Hytale Server jar from vineServerJar")
225227
}
226228

227-
jars.each { jarFile ->
228-
def jarBase = jarFile.name.replaceAll(/\.jar$/, "")
229-
def jarOut = new File(outDir, jarBase)
230-
jarOut.mkdirs()
231-
232-
def externals = jars.findAll { it != jarFile }
233-
.collectMany { ["-e", it.absolutePath] }
234-
235-
def cmd = [
236-
javaExe,
237-
"-jar", vineflowerJar.absolutePath,
238-
*externals,
239-
jarFile.absolutePath,
240-
jarOut.absolutePath
241-
]
229+
def filteredJar = new File(tempDir, "Server-com-hytale-only.jar")
230+
if (filteredJar.exists()) {
231+
filteredJar.delete()
232+
}
242233

243-
logger.lifecycle("Running: ${cmd.join(' ')}")
234+
// Build a temp jar containing only com/hytale classes/resources
235+
ant.jar(destfile: filteredJar.absolutePath) {
236+
zipfileset(src: serverJar.absolutePath) {
237+
include(name: "com/hytale/**")
238+
}
239+
}
244240

245-
def pb = new ProcessBuilder(cmd)
246-
pb.directory(project.projectDir)
247-
pb.redirectErrorStream(true)
241+
if (!filteredJar.exists() || filteredJar.length() == 0) {
242+
throw new GradleException("Filtered jar is empty. Nothing matched com/hytale/** inside ${serverJar.name}")
243+
}
248244

249-
def proc = pb.start()
250-
proc.inputStream.withReader { r ->
251-
r.eachLine { line -> logger.lifecycle(line) }
252-
}
245+
def externals = classpathConfig.incoming.artifactView { it.lenient(true) }
246+
.files.files
247+
.findAll { it.name.endsWith(".jar") && it != serverJar }
248+
.collectMany { ["-e", it.absolutePath] }
249+
250+
def cmd = [
251+
javaExe,
252+
"-jar", vineflowerJar.absolutePath,
253+
*externals,
254+
filteredJar.absolutePath,
255+
outDir.absolutePath
256+
]
257+
258+
logger.lifecycle("Running: ${cmd.join(' ')}")
259+
260+
def pb = new ProcessBuilder(cmd)
261+
pb.directory(project.projectDir)
262+
pb.redirectErrorStream(true)
263+
264+
def proc = pb.start()
265+
proc.inputStream.withReader { r ->
266+
r.eachLine { line -> logger.lifecycle(line) }
267+
}
253268

254-
def exit = proc.waitFor()
255-
if (exit != 0) {
256-
logger.lifecycle("Vineflower failed for ${jarFile.name} (exit ${exit})")
257-
} else {
258-
logger.lifecycle("Decompiled ${jarFile.name} -> ${jarOut}")
259-
}
269+
def exit = proc.waitFor()
270+
if (exit != 0) {
271+
throw new GradleException("Vineflower failed (exit ${exit})")
260272
}
273+
274+
logger.lifecycle("Decompiled com/hytale from ${serverJar.name} -> ${outDir}")
261275
}
262276
}
263277

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ org.gradle.caching = true
66

77
# Common
88
java_version = 25
9-
hytale_version = 2026.02.19-1a311a592
9+
hytale_version = 2026.03.20-db226053c
1010
release_type = RELEASE
1111
maven_url = https://maven.azuredoom.com/mods
1212

@@ -28,7 +28,7 @@ includes_pack = true
2828
disabled_by_default = false
2929
patchline = release
3030
load_user_mods = false
31-
server_version = 2026.02.19-1a311a592
31+
server_version = 2026.03.20-db226053c
3232
manifest_dependencies = Hytale:EntityModule=*,Hytale:InteractionModule=*,Hytale:DamageModule=*,Hytale:AssetModule=*,com.azuredoom:levelingcore=*
3333
manifest_opt_dependencies = Ellie:HyUI=*,org.herolias:DynamicTooltipsLib=*
3434
curseforgeID = 1486422

src/main/java/com/azuredoom/classescore/ClassesCore.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import com.azuredoom.classescore.data.ClassDefinition;
2323
import com.azuredoom.classescore.data.ClassRegistry;
2424
import com.azuredoom.classescore.exceptions.ClassesCoreException;
25-
import com.azuredoom.classescore.gameplay.services.armor.EquipBlockManager;
25+
import com.azuredoom.classescore.gameplay.services.armor.ArmorBlockClassSystem;
2626
import com.azuredoom.classescore.gameplay.services.damage.ClassDamageSystem;
2727
import com.azuredoom.classescore.gameplay.services.items.HandGateTickingSystem;
2828
import com.azuredoom.classescore.gameplay.services.items.ItemBlockPacketManager;
@@ -49,7 +49,7 @@ public class ClassesCore extends JavaPlugin {
4949
playerRestrictionCache
5050
);
5151

52-
public static final EquipBlockManager equipBlockManager = new EquipBlockManager(playerRestrictionCache);
52+
public static final ArmorBlockClassSystem equipBlockManager = new ArmorBlockClassSystem(playerRestrictionCache);
5353

5454
public ClassesCore(@NotNull JavaPluginInit init) {
5555
super(init);
@@ -98,7 +98,6 @@ protected void setup() {
9898
playerRestrictionCache.clear(playerId);
9999
ClassesCoreAPI.getClassServiceIfPresent().ifPresent(service -> service.evictPlayer(playerId));
100100
});
101-
ClassesCore.equipBlockManager.start();
102101
}
103102
if (PluginManager.get().getPlugin(new PluginIdentifier("org.herolias", "DynamicTooltipsLib")) != null) {
104103
DynamicTooltipsLibCompat.register();
@@ -125,7 +124,6 @@ protected void start() {
125124
protected void shutdown() {
126125
if (config.get().isEnableClassItemRestrictions()) {
127126
itemBlockPacketManager.shutdown();
128-
equipBlockManager.shutdown();
129127
}
130128
var bootstrap = new ClassesBootstrap(this, config.get()).bootstrap();
131129
try {
@@ -139,6 +137,7 @@ public void registerAllSystems() {
139137
getEntityStoreRegistry().registerSystem(
140138
new HandGateTickingSystem(itemBlockPacketManager.getHandCheckState(), playerRestrictionCache)
141139
);
140+
getEntityStoreRegistry().registerSystem(equipBlockManager);
142141
getEntityStoreRegistry().registerSystem(new ClassDamageSystem());
143142
getEntityStoreRegistry().registerSystem(new StatsTickingSystem());
144143
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package com.azuredoom.classescore.gameplay.services.armor;
2+
3+
import com.azuredoom.levelingcore.systems.equipment.ArmorBlockLevelSystem;
4+
import com.hypixel.hytale.server.core.HytaleServer;
5+
import com.hypixel.hytale.server.core.entity.entities.Player;
6+
import com.hypixel.hytale.server.core.inventory.ItemStack;
7+
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
8+
import com.hypixel.hytale.server.core.inventory.transaction.*;
9+
import org.jetbrains.annotations.NotNull;
10+
import org.jetbrains.annotations.Nullable;
11+
12+
import java.util.Set;
13+
import java.util.concurrent.TimeUnit;
14+
import javax.annotation.Nonnull;
15+
16+
import com.azuredoom.classescore.gameplay.services.items.PlayerRestrictionCache;
17+
import com.azuredoom.classescore.util.NotificationsUtil;
18+
19+
public class ArmorBlockClassSystem extends ArmorBlockLevelSystem {
20+
21+
private final PlayerRestrictionCache restrictionCache;
22+
23+
public ArmorBlockClassSystem(PlayerRestrictionCache restrictionCache) {
24+
super();
25+
this.restrictionCache = restrictionCache;
26+
}
27+
28+
@Override
29+
protected void rollbackArmorTransaction(
30+
@NotNull Player player,
31+
@NotNull ItemContainer armorContainer,
32+
@Nullable Transaction transaction,
33+
@NotNull Set<String> refundedKeys
34+
) {
35+
if (transaction == null || !transaction.succeeded()) {
36+
return;
37+
}
38+
39+
switch (transaction) {
40+
case MoveTransaction<?> moveTransaction -> {
41+
if (moveTransaction.getMoveType() == MoveType.MOVE_TO_SELF) {
42+
rollbackArmorTransaction(player, armorContainer, moveTransaction.getAddTransaction(), refundedKeys);
43+
}
44+
}
45+
case ListTransaction<?> listTransaction -> {
46+
for (var nested : listTransaction.getList()) {
47+
rollbackArmorTransaction(player, armorContainer, nested, refundedKeys);
48+
}
49+
}
50+
case ItemStackTransaction itemStackTransaction -> {
51+
for (var slotTransaction : itemStackTransaction.getSlotTransactions()) {
52+
rollbackArmorTransaction(player, armorContainer, slotTransaction, refundedKeys);
53+
}
54+
}
55+
case SlotTransaction slotTransaction -> {
56+
var before = slotTransaction.getSlotBefore();
57+
var after = slotTransaction.getSlotAfter();
58+
59+
if (after == null || ItemStack.isEmpty(after)) {
60+
return;
61+
}
62+
63+
if (sameStack(before, after)) {
64+
return;
65+
}
66+
67+
var itemId = after.getItemId();
68+
if (itemId.isEmpty()) {
69+
return;
70+
}
71+
72+
var playerId = player.getUuid();
73+
if (restrictionCache.canUseArmor(playerId, itemId)) {
74+
return;
75+
}
76+
77+
var classId = restrictionCache.getClassId(playerId).orElse("Unknown");
78+
79+
NotificationsUtil.sendItemClassRestrictionNotification(
80+
player.getPlayerRef(),
81+
after,
82+
classId
83+
);
84+
85+
var swapping = (before != null && !ItemStack.isEmpty(before));
86+
87+
armorContainer.setItemStackForSlot(slotTransaction.getSlot(), before, true);
88+
89+
var key = "armorSlot:" + slotTransaction.getSlot();
90+
if (refundedKeys.add(key)) {
91+
giveOrDrop(player, after);
92+
93+
if (swapping) {
94+
var removeOne = oneOf(before);
95+
player.getInventory().getCombinedHotbarFirst().removeItemStack(removeOne, false, true);
96+
}
97+
}
98+
}
99+
default -> {}
100+
}
101+
}
102+
103+
@Override
104+
public void validateArmorOnReady(@Nonnull Player player) {
105+
ignoreArmorEvents.add(player.getUuid());
106+
HytaleServer.SCHEDULED_EXECUTOR.schedule(
107+
() -> ignoreArmorEvents.remove(player.getUuid()),
108+
500L,
109+
TimeUnit.MILLISECONDS
110+
);
111+
112+
var inventory = player.getInventory();
113+
var armor = inventory.getArmor();
114+
if (armor == null) {
115+
return;
116+
}
117+
118+
var playerId = player.getUuid();
119+
120+
restoringArmor = true;
121+
try {
122+
var capacity = armor.getCapacity();
123+
for (short slot = 0; slot < capacity; slot++) {
124+
var stack = armor.getItemStack(slot);
125+
if (stack == null || ItemStack.isEmpty(stack)) {
126+
continue;
127+
}
128+
129+
var itemId = stack.getItemId();
130+
if (itemId.isEmpty()) {
131+
continue;
132+
}
133+
134+
if (restrictionCache.canUseArmor(playerId, itemId)) {
135+
continue;
136+
}
137+
138+
var classId = restrictionCache.getClassId(playerId).orElse("Unknown");
139+
140+
NotificationsUtil.sendItemClassRestrictionNotification(
141+
player.getPlayerRef(),
142+
stack,
143+
classId
144+
);
145+
146+
armor.setItemStackForSlot(slot, null, true);
147+
giveOrDrop(player, stack);
148+
}
149+
} finally {
150+
restoringArmor = false;
151+
}
152+
}
153+
}

0 commit comments

Comments
 (0)