Files
EndlessJumper/src/main/java/eu/mhsl/minecraft/endlessjumper/EndlessJumper.java
T

154 lines
7.2 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package eu.mhsl.minecraft.endlessjumper;
import eu.mhsl.minecraft.endlessjumper.generator.JumpGenerator;
import net.minestom.server.Auth;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.command.builder.arguments.ArgumentWord;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.PlayerSkin;
import net.minestom.server.event.GlobalEventHandler;
import net.minestom.server.event.item.ItemDropEvent;
import net.minestom.server.event.item.PickupItemEvent;
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
import net.minestom.server.event.player.PlayerBlockBreakEvent;
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
import net.minestom.server.event.player.PlayerChatEvent;
import net.minestom.server.event.player.PlayerDisconnectEvent;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.event.player.PlayerSpawnEvent;
import net.minestom.server.event.player.PlayerSwapItemEvent;
import net.minestom.server.event.player.PlayerUseItemEvent;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.instance.block.Block;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class EndlessJumper {
private static final Pos START = new Pos(0, 100, 0);
private static final Pos SPAWN = START.add(0.5, 1, 0.5);
private static final Map<Player, JumpGenerator> games = new ConcurrentHashMap<>();
public static void main(String[] args) throws IOException {
MinecraftServer server = MinecraftServer.init(new Auth.Velocity(readForwardingSecret()));
Progress.load();
Admins.load();
GlobalEventHandler eventHandler = MinecraftServer.getGlobalEventHandler();
PlayerSkin skin = PlayerSkin.fromUsername("Notch");
eventHandler.addListener(AsyncPlayerConfigurationEvent.class, event -> {
final Player p = event.getPlayer();
p.setRespawnPoint(SPAWN);
p.setSkin(skin);
event.setSpawningInstance(createWorld());
});
eventHandler.addListener(PlayerSpawnEvent.class, event -> {
if(!event.isFirstSpawn()) return;
Player p = event.getPlayer();
p.setCanPickupItem(true);
ScoreTab.join(p);
Jokers.clear(p);
// sobald der Spieler in der Welt steht, geht es los
games.put(p, new JumpGenerator(START, Progress.checkpoint(p.getUuid()), p, p.getInstance()));
});
eventHandler.addListener(PlayerMoveEvent.class, event -> {
JumpGenerator generator = games.get(event.getPlayer());
if(generator == null) event.setCancelled(true);
else generator.onMove(event.getNewPosition());
});
eventHandler.addListener(PlayerBlockBreakEvent.class, event -> event.setCancelled(true));
eventHandler.addListener(PlayerBlockPlaceEvent.class, event -> event.setCancelled(true));
eventHandler.addListener(ItemDropEvent.class, event -> event.setCancelled(true));
eventHandler.addListener(PlayerSwapItemEvent.class, event -> event.setCancelled(true));
// den Chat verteilt Velocity (chat-bridge) an das ganze Netzwerk, hier würde er doppelt stehen
eventHandler.addListener(PlayerChatEvent.class, event -> event.setCancelled(true));
// der Client meldet Rechtsklicks auch auf Blöcke als Item-Nutzung, solange der Block sie nicht
// selbst verarbeitet (auf der Strecke nie) – ein Event genügt, Joker werden nicht doppelt verbraucht
eventHandler.addListener(PlayerUseItemEvent.class, event -> {
JumpGenerator generator = games.get(event.getPlayer());
Jokers joker = Jokers.of(event.getItemStack());
if(generator != null && joker != null && generator.useJoker(joker)) joker.take(event.getPlayer());
});
eventHandler.addListener(PickupItemEvent.class, event -> {
Jokers joker = Jokers.of(event.getItemStack());
if(event.getLivingEntity() instanceof Player player && joker != null) joker.add(player);
else event.setCancelled(true);
});
eventHandler.addListener(PlayerDisconnectEvent.class, event -> {
Player p = event.getPlayer();
games.remove(p);
ScoreTab.leave(p);
// jede Partie hat eine eigene Welt: nach dem Verlassen freigeben
releaseLater(p.getInstance());
});
Command reset = new Command("reset");
reset.setDefaultExecutor((sender, context) -> {
if(sender instanceof Player player) resetGame(player);
});
MinecraftServer.getCommandManager().register(reset);
// nur für Admins (admins.txt): Joker zum Testen direkt geben
Command joker = new Command("joker");
joker.setCondition((sender, commandString) -> sender instanceof Player player && Admins.is(player));
ArgumentWord type = ArgumentType.Word("typ").from(Jokers.keys());
joker.addSyntax((sender, context) -> Jokers.byKey(context.get(type)).add((Player) sender), type);
MinecraftServer.getCommandManager().register(joker);
server.start("0.0.0.0", 25501);
}
private static InstanceContainer createWorld() {
InstanceContainer world = MinecraftServer.getInstanceManager().createInstanceContainer();
world.setBlock(START, Block.DIAMOND_BLOCK);
// Tageszeit steuert der jeweilige Abschnitt
world.defaultClock().pause();
return world;
}
private static void releaseLater(Instance world) {
if(world != null) MinecraftServer.getSchedulerManager().scheduleNextTick(() -> MinecraftServer.getInstanceManager().unregisterInstance(world));
}
/** /reset: Fortschritt, Strecke, Score und Joker zurücksetzen und in einer frischen Welt bei 0 beginnen. */
private static void resetGame(Player player) {
JumpGenerator old = games.remove(player);
if(old != null) old.close();
Progress.reset(player.getUuid());
Instance oldWorld = player.getInstance();
InstanceContainer world = createWorld();
player.setVelocity(Vec.ZERO);
player.setInstance(world, SPAWN).thenRun(() -> {
Jokers.clear(player);
games.put(player, new JumpGenerator(START, 0, player, world));
releaseLater(oldWorld);
});
}
// Wie bei Velocity: Secret aus forwarding.secret im Arbeitsverzeichnis, alternativ per Umgebungsvariable
private static String readForwardingSecret() throws IOException {
String fromEnv = System.getenv("VELOCITY_FORWARDING_SECRET");
if(fromEnv != null && !fromEnv.isBlank()) return fromEnv.trim();
Path file = Path.of("forwarding.secret");
if(!Files.exists(file))
throw new IllegalStateException("forwarding.secret fehlt im Arbeitsverzeichnis (oder VELOCITY_FORWARDING_SECRET setzen)");
return Files.readString(file).trim();
}
}