Improve Advancements performance

This commit is contained in:
2026-06-12 14:19:47 +02:00
parent 9e43d5cd1b
commit 914134600e
3 changed files with 159 additions and 63 deletions
@@ -29,30 +29,32 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import de.steamwar.messages.Chatter; import de.steamwar.messages.Chatter;
import de.steamwar.sql.EventFight;
import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.SteamwarUser;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.ToString; import lombok.ToString;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.Date; import java.util.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
@RequiredArgsConstructor @RequiredArgsConstructor
public class Advancement { public class Advancement {
protected static final Map<SteamwarUser, Map<Advancement.Value.Key, Advancement.Value>> values = new HashMap<>();
{ {
Advancements.all.add(this); Advancements.all.add(this);
} }
protected final Map<SteamwarUser, Advancement.Data> data = new HashMap<>(); protected final Map<SteamwarUser, Advancement.Data> data = new HashMap<>();
protected final Map<SteamwarUser, Advancement.Value> value = new HashMap<>();
public Advancement.Data get(SteamwarUser user) { public Advancement.Data get(SteamwarUser user) {
return get(user, Data::new); return get(user, Data::new);
@@ -143,6 +145,46 @@ public class Advancement {
public abstract boolean hidden(Data data); public abstract boolean hidden(Data data);
} }
public static class Value<T extends Number> {
@AllArgsConstructor
public static class Key<T extends Number> {
public static final List<Key> keys = new ArrayList<>();
{
keys.add(this);
}
private final Function<SteamwarUser, T> valueFunction;
public T get(SteamwarUser user) {
return valueFunction.apply(user);
}
public Function<SteamwarUser, Integer> max(int neededValue) {
return user -> {
double value = values.get(user).get(Key.this).value.doubleValue();
if (value > neededValue) return Math.min(neededValue, 100);
return (int) (value / Math.max(neededValue / 100.0, 1));
};
}
public Function<SteamwarUser, Integer> reached(int neededValue) {
return user -> {
double value = values.get(user).get(Key.this).value.doubleValue();
return value >= neededValue ? 1 : 0;
};
}
}
@Getter
private T value;
public void update(SteamwarUser user, Key<T> key) {
this.value = key.valueFunction.apply(user);
}
}
@ToString @ToString
public static class Data { public static class Data {
private final Advancement advancement; private final Advancement advancement;
@@ -42,6 +42,65 @@ public class Advancements {
@Getter @Getter
private static final List<Advancement> playtime = new ArrayList<>(); private static final List<Advancement> playtime = new ArrayList<>();
public static final Advancement.Value.Key<Double> PLAY_TIME_KEY = new Advancement.Value.Key<>(user -> {
double playtime = user.getOnlinetime();
playtime += Instant.now().getEpochSecond() - Storage.sessions.get(Chatter.of(user).getPlayer()).toInstant().getEpochSecond();
playtime /= 60d * 60d;
return playtime;
});
public static final Advancement.Value.Key<Long> FIGHT_COUNT = new Advancement.Value.Key<>(user -> {
return FightPlayer.countFights(user.getId());
});
public static final Advancement.Value.Key<Integer> FIGHT_COUNT_WAR_GEAR = new Advancement.Value.Key<>(user -> {
return FightPlayer.countFights(user.getId(), "WarGear");
});
public static final Advancement.Value.Key<Integer> FIGHT_COUNT_MINI_WAR_GEAR = new Advancement.Value.Key<>(user -> {
return FightPlayer.countFights(user.getId(), "MiniWarGear");
});
public static final Advancement.Value.Key<Integer> FIGHT_COUNT_WAR_SHIP = new Advancement.Value.Key<>(user -> {
return FightPlayer.countFights(user.getId(), "WarShip");
});
public static final Advancement.Value.Key<Long> EVENT_FIGHT_COUNT = new Advancement.Value.Key<>(user -> {
return EventFight.countEventFights(user);
});
public static final Advancement.Value.Key<Integer> EVENT_FIGHT_FIRST_PLACE_COUNT = new Advancement.Value.Key<>(user -> {
return EventFight.countPlacement(user, 1);
});
public static final Advancement.Value.Key<Integer> EVENT_FIGHT_SECOND_PLACE_COUNT = new Advancement.Value.Key<>(user -> {
return EventFight.countPlacement(user, 2);
});
public static final Advancement.Value.Key<Integer> EVENT_FIGHT_THIRDPLACE_COUNT = new Advancement.Value.Key<>(user -> {
return EventFight.countPlacement(user, 3);
});
public static final Advancement.Value.Key<Long> CHECKED_SCHEMATIC_COUNT = new Advancement.Value.Key<>(user -> {
return CheckedSchematic.countChecked(user);
});
public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT = new Advancement.Value.Key<>(user -> {
return CheckedSchematic.countAccepted(user);
});
public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT_WAR_GEAR = new Advancement.Value.Key<>(user -> {
return CheckedSchematic.countAccepted(user, "WarGear");
});
public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT_MINI_WAR_GEAR = new Advancement.Value.Key<>(user -> {
return CheckedSchematic.countAccepted(user, "MiniWarGear");
});
public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT_WAR_SHIP = new Advancement.Value.Key<>(user -> {
return CheckedSchematic.countAccepted(user, "WarShip");
});
public static final Advancement ROOT = new Advancement( public static final Advancement ROOT = new Advancement(
"steamwar:advancements/root", "steamwar:advancements/root",
Optional.empty(), Optional.empty(),
@@ -77,14 +136,7 @@ public class Advancements {
), ),
Advancement.HidePolicy.PREVIOUS_UNFINISHED, Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(neededPlayTime, 100), Math.min(neededPlayTime, 100),
user -> { PLAY_TIME_KEY.max(neededPlayTime)
double playtime = user.getOnlinetime();
playtime += Instant.now().getEpochSecond() - Storage.sessions.get(Chatter.of(user).getPlayer()).toInstant().getEpochSecond();
playtime /= 60d * 60d;
if (playtime > neededPlayTime) return Math.min(neededPlayTime, 100);
return (int) (playtime / Math.max(neededPlayTime / 100.0, 1));
}
); );
playtime.add(previous); playtime.add(previous);
} }
@@ -108,22 +160,18 @@ public class Advancements {
), ),
Advancement.HidePolicy.PREVIOUS_UNFINISHED, Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(fightCount, 100), Math.min(fightCount, 100),
user -> { FIGHT_COUNT.max(fightCount)
long fights = FightPlayer.countFights(user.getId());
if (fights > fightCount) return Math.min(fightCount, 100);
return (int) (fights / Math.max(fightCount / 100.0, 1));
}
); );
if (i == 0) { if (i == 0) {
fightsPerType(previous, 5f, "WarGear", "stone_bricks"); fightsPerType(previous, 5f, "WarGear", FIGHT_COUNT_WAR_GEAR, "stone_bricks");
fightsPerType(previous, 6f, "MiniWarGear", "stone_brick_slab"); fightsPerType(previous, 6f, "MiniWarGear", FIGHT_COUNT_MINI_WAR_GEAR, "stone_brick_slab");
fightsPerType(previous, 7f, "WarShip", "dark_oak_boat"); fightsPerType(previous, 7f, "WarShip", FIGHT_COUNT_WAR_SHIP, "dark_oak_boat");
} }
} }
} }
private static void fightsPerType(Advancement previous, float yCoord, String type, String item) { private static void fightsPerType(Advancement previous, float yCoord, String type, Advancement.Value.Key<Integer> typeKey, String item) {
int[] fightCounts = new int[]{1, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000}; int[] fightCounts = new int[]{1, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000};
for (int i = 0; i < fightCounts.length; i++) { for (int i = 0; i < fightCounts.length; i++) {
int fightCount = fightCounts[i]; int fightCount = fightCounts[i];
@@ -140,11 +188,7 @@ public class Advancements {
), ),
i == 0 ? Advancement.HidePolicy.WITH_PREVIOUS : Advancement.HidePolicy.PREVIOUS_UNFINISHED, i == 0 ? Advancement.HidePolicy.WITH_PREVIOUS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(fightCount, 100), Math.min(fightCount, 100),
user -> { typeKey.max(fightCount)
long fights = FightPlayer.countFights(user.getId(), type);
if (fights > fightCount) return Math.min(fightCount, 100);
return (int) (fights / Math.max(fightCount / 100.0, 1));
}
); );
} }
} }
@@ -167,22 +211,18 @@ public class Advancements {
), ),
Advancement.HidePolicy.PREVIOUS_UNFINISHED, Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(eventFightCount, 100), Math.min(eventFightCount, 100),
user -> { EVENT_FIGHT_COUNT.max(eventFightCount)
long fights = EventFight.countEventFights(user);
if (fights > eventFightCount) return Math.min(eventFightCount, 100);
return (int) (fights / Math.max(eventFightCount / 100.0, 1));
}
); );
if (i == 0) { if (i == 0) {
placementsCounts(previous, 9f, 1, "gold_block", Advancement.Display.FrameType.CHALLENGE); placementsCounts(previous, 9f, 1, "gold_block", EVENT_FIGHT_FIRST_PLACE_COUNT, Advancement.Display.FrameType.CHALLENGE);
placementsCounts(previous, 10f, 2, "iron_block", Advancement.Display.FrameType.GOAL); placementsCounts(previous, 10f, 2, "iron_block", EVENT_FIGHT_SECOND_PLACE_COUNT, Advancement.Display.FrameType.GOAL);
placementsCounts(previous, 11f, 3, "copper_block", Advancement.Display.FrameType.TASK); placementsCounts(previous, 11f, 3, "copper_block", EVENT_FIGHT_THIRDPLACE_COUNT, Advancement.Display.FrameType.TASK);
} }
} }
} }
private static void placementsCounts(Advancement previous, float yCoord, int placement, String item, Advancement.Display.FrameType frameType) { private static void placementsCounts(Advancement previous, float yCoord, int placement, String item, Advancement.Value.Key<Integer> typeKey, Advancement.Display.FrameType frameType) {
for (int placementCount = 1; placementCount <= 10; placementCount++) { for (int placementCount = 1; placementCount <= 10; placementCount++) {
int finalPlacementCount = placementCount; int finalPlacementCount = placementCount;
previous = new Advancement( previous = new Advancement(
@@ -198,7 +238,7 @@ public class Advancements {
), ),
placementCount == 1 ? Advancement.HidePolicy.WITH_PREVIOUS : Advancement.HidePolicy.PREVIOUS_UNFINISHED, placementCount == 1 ? Advancement.HidePolicy.WITH_PREVIOUS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
1, 1,
user -> EventFight.countPlacement(user, placement) >= finalPlacementCount ? 1 : 0 typeKey.reached(placementCount)
); );
} }
} }
@@ -221,11 +261,7 @@ public class Advancements {
), ),
i == 0 ? Advancement.HidePolicy.NO_PROGRESS : Advancement.HidePolicy.PREVIOUS_UNFINISHED, i == 0 ? Advancement.HidePolicy.NO_PROGRESS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(checkedCount, 100), Math.min(checkedCount, 100),
user -> { CHECKED_SCHEMATIC_COUNT.max(checkedCount)
long checked = CheckedSchematic.countChecked(user);
if (checked > checkedCount) return Math.min(checkedCount, 100);
return (int) (checked / Math.max(checkedCount / 100.0, 1));
}
); );
} }
} }
@@ -248,22 +284,18 @@ public class Advancements {
), ),
Advancement.HidePolicy.PREVIOUS_UNFINISHED, Advancement.HidePolicy.PREVIOUS_UNFINISHED,
Math.min(acceptedCount, 100), Math.min(acceptedCount, 100),
user -> { ACCEPTED_SCHEMATIC_COUNT.max(acceptedCount)
long accepted = CheckedSchematic.countAccepted(user);
if (accepted > acceptedCount) return Math.min(acceptedCount, 100);
return (int) (accepted / Math.max(acceptedCount / 100.0, 1));
}
); );
if (i == 0) { if (i == 0) {
acceptedPerType(previous, 2f, "WarGear", "end_stone_bricks"); acceptedPerType(previous, 2f, "WarGear", ACCEPTED_SCHEMATIC_COUNT_WAR_GEAR, "end_stone_bricks");
acceptedPerType(previous, 3f, "MiniWarGear", "end_stone_brick_slab"); acceptedPerType(previous, 3f, "MiniWarGear", ACCEPTED_SCHEMATIC_COUNT_MINI_WAR_GEAR, "end_stone_brick_slab");
acceptedPerType(previous, 4f, "WarShip", "oak_boat"); acceptedPerType(previous, 4f, "WarShip", ACCEPTED_SCHEMATIC_COUNT_WAR_SHIP, "oak_boat");
} }
} }
} }
private static void acceptedPerType(Advancement previous, float xCoord, String type, String item) { private static void acceptedPerType(Advancement previous, float xCoord, String type, Advancement.Value.Key<Long> typeKey, String item) {
new Advancement( new Advancement(
"steamwar:advancements/accepted_" + type, "steamwar:advancements/accepted_" + type,
Optional.of(previous), Optional.of(previous),
@@ -277,7 +309,7 @@ public class Advancements {
), ),
Advancement.HidePolicy.WITH_PREVIOUS, Advancement.HidePolicy.WITH_PREVIOUS,
1, 1,
user -> CheckedSchematic.countAccepted(user, type) > 0 ? 1 : 0 typeKey.reached(1)
); );
} }
} }
@@ -35,6 +35,7 @@ import de.steamwar.velocitycore.listeners.BasicListener;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -46,16 +47,11 @@ public class AdvancementsManager extends BasicListener {
static { static {
selectAdvancementTabPacket = new SelectAdvancementTabPacket(Optional.of("steamwar:advancements/root")); selectAdvancementTabPacket = new SelectAdvancementTabPacket(Optional.of("steamwar:advancements/root"));
try { registerPacketId(ProtocolVersion.MINECRAFT_1_21_9, 0x53, 0x80);
StateRegistry.PacketRegistry.ProtocolRegistry registry = StateRegistry.PLAY.getProtocolRegistry(ProtocolUtils.Direction.CLIENTBOUND, ProtocolVersion.MINECRAFT_1_21_6); registerPacketId(ProtocolVersion.MINECRAFT_1_21_7, 0x4E, 0x7B);
Field field = StateRegistry.PacketRegistry.ProtocolRegistry.class.getDeclaredField("packetClassToId"); registerPacketId(ProtocolVersion.MINECRAFT_1_21_6, 0x4E, 0x7B);
field.setAccessible(true); registerPacketId(ProtocolVersion.MINECRAFT_1_21_5, 0x4E, 0x7B);
Object2IntMap<Class<? extends MinecraftPacket>> map = (Object2IntMap) field.get(registry); registerPacketId(ProtocolVersion.MINECRAFT_1_21_4, 0x4F, 0x7B);
map.put(SelectAdvancementTabPacket.class, 0x4E);
map.put(Advancement.Packet.class, 0x7B);
} catch (Exception e) {
// Ignore
}
VelocityCore.schedule(() -> { VelocityCore.schedule(() -> {
for (Advancement advancement : Advancements.getPlaytime()) { for (Advancement advancement : Advancements.getPlaytime()) {
@@ -66,18 +62,44 @@ public class AdvancementsManager extends BasicListener {
}).repeat(10, TimeUnit.MINUTES).schedule(); }).repeat(10, TimeUnit.MINUTES).schedule();
} }
private static void registerPacketId(ProtocolVersion version, int selectAdvancementTabPacket, int advancementPacket) {
try {
StateRegistry.PacketRegistry.ProtocolRegistry registry = StateRegistry.PLAY.getProtocolRegistry(ProtocolUtils.Direction.CLIENTBOUND, version);
Field field = StateRegistry.PacketRegistry.ProtocolRegistry.class.getDeclaredField("packetClassToId");
field.setAccessible(true);
Object2IntMap<Class<? extends MinecraftPacket>> map = (Object2IntMap) field.get(registry);
map.put(SelectAdvancementTabPacket.class, selectAdvancementTabPacket);
map.put(Advancement.Packet.class, advancementPacket);
} catch (Exception e) {
// Ignore
}
}
@Subscribe(priority = -1000) @Subscribe(priority = -1000)
public void onPostLogin(PostLoginEvent event) { public void onPostLogin(PostLoginEvent event) {
// Only enable for 1.21.4 or higher
if (event.getPlayer().getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_21_4)) {
return;
}
((ConnectedPlayer) event.getPlayer()).getConnection().write(selectAdvancementTabPacket); ((ConnectedPlayer) event.getPlayer()).getConnection().write(selectAdvancementTabPacket);
SteamwarUser user = SteamwarUser.get(event.getPlayer().getUniqueId());
Advancement.Value.Key.keys.forEach(key -> {
Advancement.Value value = new Advancement.Value();
value.update(user, key);
Advancement.values.computeIfAbsent(user, k -> new HashMap<>()).put(key, value);
});
for (Advancement advancement : Advancements.getAll()) { for (Advancement advancement : Advancements.getAll()) {
advancement.get(SteamwarUser.get(event.getPlayer().getUniqueId())); advancement.get(user);
} }
} }
@Subscribe @Subscribe
public void onDisconnect(DisconnectEvent event) { public void onDisconnect(DisconnectEvent event) {
SteamwarUser user = SteamwarUser.get(event.getPlayer().getUniqueId());
for (Advancement advancement : Advancements.getAll()) { for (Advancement advancement : Advancements.getAll()) {
advancement.data.remove(SteamwarUser.get(event.getPlayer().getUniqueId())); advancement.data.remove(user);
} }
Advancement.values.remove(user);
} }
} }