diff --git a/DockerFiles/Dockerfile.game_server b/DockerFiles/Dockerfile.game_server index a684e7eb1..331bdf94b 100644 --- a/DockerFiles/Dockerfile.game_server +++ b/DockerFiles/Dockerfile.game_server @@ -28,11 +28,9 @@ RUN curl -fSL -o /tmp/worlds.tar.gz "https://files.catbox.moe/9uas8z.gz" && \ EXPOSE 25565 65535 8080 20000 -RUN cp configuration_files/NanoLimbo-1.10.2.jar ./NanoLimbo-1.10.2.jar && \ - cp configuration_files/settings.yml ./settings.yml && \ +RUN cp configuration_files/server.toml ./server.toml && \ cp -a configuration_files/skyblock/. configuration/skyblock/ && \ cp configuration_files/entrypoint.sh ./entrypoint.sh && \ - chmod +x entrypoint.sh && \ - sed -i "s/ip: 'localhost'/ip: '0.0.0.0'/" ./settings.yml + chmod +x entrypoint.sh CMD ["sh", "entrypoint.sh"] diff --git a/DockerFiles/Dockerfile.proxy b/DockerFiles/Dockerfile.proxy index ffa8a7d96..dba7bed15 100644 --- a/DockerFiles/Dockerfile.proxy +++ b/DockerFiles/Dockerfile.proxy @@ -2,7 +2,7 @@ FROM eclipse-temurin:25-jdk WORKDIR /app -RUN apt-get update && apt-get install -y jq expect netcat-traditional curl && apt-get clean +RUN apt-get update && apt-get install -y jq netcat-traditional curl && apt-get clean # Download Velocity proxy JAR RUN curl -fSL -o velocity.jar "https://fill-data.papermc.io/v1/objects/ef1a852bfae7397e84907837925e7ad21c6312066290edaae401b77f6f423ac3/velocity-3.4.0-SNAPSHOT-558.jar" @@ -14,22 +14,14 @@ RUN mkdir -p plugins && \ # Copy configuration data COPY ./configuration /app/configuration_files -# Run Velocity to generate forwarding.secret, then shut it down -RUN printf '#!/usr/bin/expect -f\nset timeout 120\nspawn java -jar velocity.jar\nexpect ">"\nsend "shutdown\\r"\nexpect eof\n' > /tmp/run_velocity.exp && \ - chmod +x /tmp/run_velocity.exp && \ - /tmp/run_velocity.exp && \ - rm /tmp/run_velocity.exp - # Replace generated velocity.toml with our own RUN rm -f velocity.toml && \ cp configuration_files/velocity.toml velocity.toml -# Set up config.yml with the generated forwarding secret +# Set up config.yml template RUN mkdir -p configuration && \ - cp configuration_files/config.example.yml ./configuration/config.yml && \ - secret=$(cat forwarding.secret) && \ - sed -i "s/velocity-secret: .*/velocity-secret: '$secret'/" ./configuration/config.yml + cp configuration_files/config.example.yml ./configuration/config.yml EXPOSE 25565 -CMD ["sh", "-c", "cp forwarding.secret /app/configuration_files/forwarding.secret && java -jar velocity.jar"] +CMD ["sh", "-c", "[ -n \"$FORWARDING_SECRET\" ] || { echo 'FORWARDING_SECRET is required' >&2; exit 1; }; printf '%s' \"$FORWARDING_SECRET\" > /app/forwarding.secret; sed -i \"s/velocity-secret: .*/velocity-secret: '$FORWARDING_SECRET'/\" /app/configuration/config.yml; java -jar velocity.jar"] diff --git a/commons/build.gradle.kts b/commons/build.gradle.kts index deee33312..7b1adb32f 100644 --- a/commons/build.gradle.kts +++ b/commons/build.gradle.kts @@ -26,7 +26,7 @@ dependencies { } // Must match AtlasRedisAPI's Jedis version to avoid conflicts - implementation("redis.clients:jedis:4.2.3") + implementation("redis.clients:jedis:7.2.0") - implementation("org.spongepowered:configurate-yaml:4.2.0") + implementation("de.exlll:configlib-yaml:4.8.1") } \ No newline at end of file diff --git a/commons/src/main/java/net/swofty/commons/config/ConfigProvider.java b/commons/src/main/java/net/swofty/commons/config/ConfigProvider.java index d22df7264..a702abc0b 100644 --- a/commons/src/main/java/net/swofty/commons/config/ConfigProvider.java +++ b/commons/src/main/java/net/swofty/commons/config/ConfigProvider.java @@ -1,47 +1,44 @@ package net.swofty.commons.config; +import de.exlll.configlib.ConfigurationProperties; +import de.exlll.configlib.NameFormatters; +import de.exlll.configlib.YamlConfigurationProperties; +import de.exlll.configlib.YamlConfigurations; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; -import org.spongepowered.configurate.CommentedConfigurationNode; -import org.spongepowered.configurate.yaml.NodeStyle; -import org.spongepowered.configurate.yaml.YamlConfigurationLoader; +import org.jetbrains.annotations.NotNull; import org.tinylog.Logger; -import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; public class ConfigProvider { + final static ConfigurationProperties.EnvVarResolutionConfiguration envResolution = ConfigurationProperties.EnvVarResolutionConfiguration + .resolveEnvVarsWithPrefix("HYPIXEL_", false); + + @NotNull + static final YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder() + .setNameFormatter(NameFormatters.LOWER_KEBAB_CASE) + .charset(StandardCharsets.UTF_8) + .setEnvVarResolutionConfiguration(envResolution) + .build(); + @Getter @Setter @Accessors(fluent = true) private static Settings settings; static { - try { - Logger.info("Loading config..."); - - YamlConfigurationLoader loader = YamlConfigurationLoader.builder() - .path(Path.of("./configuration/config.yml")) - .nodeStyle(NodeStyle.BLOCK) - .build(); - - CommentedConfigurationNode root = loader.load(); - CommentedConfigurationNode defaults = loader.createNode(); - defaults.set(Settings.class, new Settings()); - root.mergeFrom(defaults); - - Settings loaded = root.get(Settings.class); - if (loaded == null) { - loaded = new Settings(); - } - - loader.save(root); - settings(loaded); - } catch (IOException e) { - throw new RuntimeException("Failed to load configuration", e); - } + Logger.info("Loading config..."); + settings( + YamlConfigurations.update( + Path.of("./configuration/config.yml"), + Settings.class, + properties + ) + ); } } diff --git a/commons/src/main/java/net/swofty/commons/config/Settings.java b/commons/src/main/java/net/swofty/commons/config/Settings.java index 45c127b94..8bde0920e 100644 --- a/commons/src/main/java/net/swofty/commons/config/Settings.java +++ b/commons/src/main/java/net/swofty/commons/config/Settings.java @@ -1,72 +1,76 @@ package net.swofty.commons.config; +import de.exlll.configlib.Comment; +import de.exlll.configlib.Configuration; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import org.spongepowered.configurate.objectmapping.ConfigSerializable; -import org.spongepowered.configurate.objectmapping.meta.Comment; + +import java.util.HashMap; +import java.util.Map; @Getter -@ConfigSerializable +@Configuration @SuppressWarnings({"unused", "FieldMayBeFinal"}) public class Settings { - private String hostName = "0.0.0.0"; - private long transferTimeout = 800; + @Comment("The host name or IP address to bind the server to") + private String hostName = "0.0.0.0"; - @Comment("The MongoDB connection URI") - private String mongodb = "mongodb://localhost"; + @Comment("The MongoDB connection URI") + private String mongodb = "mongodb://localhost"; - @Comment("The Redis connection URI") - private String redisUri = "redis://localhost:6379"; + @Comment("The Redis connection URI") + private String redisUri = "redis://localhost:6379"; - @Comment("The secret key used to authenticate with Velocity proxy") - private String velocitySecret = "ixmSUgWOgvs7"; + @Comment("The secret key used to authenticate with Velocity proxy") + private String velocitySecret = "ixmSUgWOgvs7"; - private boolean requireAuth = false; + private boolean requireAuth = false; - @Comment("Whether to enable sandbox features (such as editing items)") - private boolean sandbox = false; + @Comment("Whether to enable sandbox features (such as editing items)") + private boolean sandbox = false; - @Comment("Integrations with services") - private IntegrationSettings integrations = new IntegrationSettings(); + @Comment("Integrations with services") + private IntegrationSettings integrations = new IntegrationSettings(); - @Comment("Settings related to configuration of Limbo server connections") - private LimboSettings limbo = new LimboSettings(); + @Comment("Settings related to configuration of Limbo server connections") + private LimboSettings limbo = new LimboSettings(); - @Comment("Resource pack settings keyed by pack name (e.g. testingpack, bedwarspack)") - private java.util.Map resourcePacks = new java.util.HashMap<>(); + @Comment("Resource pack settings keyed by pack name (e.g. testingpack, bedwarspack)") + private Map resourcePacks = new HashMap<>(); - @Getter - @ConfigSerializable - @NoArgsConstructor(access = AccessLevel.PRIVATE) - public static class LimboSettings { - private String hostName = "127.0.0.1"; - private int port = 65535; - } + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class LimboSettings { + private String hostName = "127.0.0.1"; + private int port = 65535; + } - @Getter - @ConfigSerializable - @NoArgsConstructor - public static class ResourcePackSettings { - @Comment("Base URL of the pack server (e.g. http://0.0.0.0:7270)") - private String serverUrl = "http://127.0.0.1:7270"; - } + @Getter + @Configuration + @NoArgsConstructor + public static class ResourcePackSettings { + @Comment("Base URL of the pack server (e.g. http://0.0.0.0:7270)") + private String serverUrl = "http://127.0.0.1:7270"; + } - @Getter - @ConfigSerializable - @NoArgsConstructor(access = AccessLevel.PRIVATE) - public static class IntegrationSettings { - @Comment("Whether to enable Spark for performance monitoring") - private boolean spark = false; + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class IntegrationSettings { + @Comment("Whether to enable Spark for performance monitoring") + private boolean spark = false; - @Comment("Whether to enable anti-cheat measures") - private boolean anticheat = false; + @Comment("Whether to enable anti-cheat measures") + private boolean anticheat = false; - @Comment("Whether to enable ViaVersion for supporting multiple Minecraft versions. This may cause issues of any kind") - private boolean viaVersion = false; + @Comment("Whether to enable ViaVersion for supporting multiple Minecraft versions. This may cause issues of any kind") + private boolean viaVersion = false; - private String sentryDsn = ""; - } + @Comment("The DSN to use for Sentry error tracking. If empty, Sentry will be disabled") + private String sentryDsn = ""; + } } diff --git a/configuration/NanoLimbo-1.10.2.jar b/configuration/NanoLimbo-1.10.2.jar deleted file mode 100644 index ff37f683d..000000000 Binary files a/configuration/NanoLimbo-1.10.2.jar and /dev/null differ diff --git a/configuration/PicoLimbo.jar b/configuration/PicoLimbo.jar new file mode 100644 index 000000000..612cc98c2 Binary files /dev/null and b/configuration/PicoLimbo.jar differ diff --git a/configuration/entrypoint.sh b/configuration/entrypoint.sh index 31c8b8b9d..f932b1ea3 100644 --- a/configuration/entrypoint.sh +++ b/configuration/entrypoint.sh @@ -1,7 +1,15 @@ #!/bin/bash -# Copy the Forwarding Secret -cp configuration_files/forwarding.secret ./forwarding.secret +if [ -n "$FORWARDING_SECRET" ]; then + secret="$FORWARDING_SECRET" +elif [ -f ./configuration_files/forwarding.secret ]; then + secret=$(cat ./configuration_files/forwarding.secret) +else + echo "FORWARDING_SECRET is required (env var or configuration_files/forwarding.secret)" >&2 + exit 1 +fi + +printf '%s' "$secret" > ./forwarding.secret # Ensure configuration/config.yml exists mkdir -p ./configuration @@ -10,14 +18,7 @@ if [ ! -f ./configuration/config.yml ]; then fi # Update config.yml with the forwarding secret (velocity-secret) -secret=$(cat ./forwarding.secret) sed -i "s/velocity-secret: .*/velocity-secret: '$secret'/" ./configuration/config.yml -# Replace the secret in settings.yml -sed -i "s/secret: '.*'/secret: '$secret'/" ./settings.yml - -# Set the settings.yml bind: ip: 'localhost' to bind: ip: '0.0.0.0' -sed -i "s/ip: 'localhost'/ip: '0.0.0.0'/" ./settings.yml - echo "$SERVICE_CMD" exec $SERVICE_CMD \ No newline at end of file diff --git a/configuration/server.toml b/configuration/server.toml new file mode 100644 index 000000000..ba61d89f5 --- /dev/null +++ b/configuration/server.toml @@ -0,0 +1,77 @@ +# configuration for PicoLimbo +bind = "0.0.0.0:65535" +welcome_message = "" +action_bar = "" +default_game_mode = "survival" +hardcore = true +fetch_player_skins = false +reduced_debug_info = false +allow_unsupported_versions = false +allow_flight = false +accept_transfers = false + +[forwarding] +method = "MODERN" +secret = "${FORWARDING_SECRET}" + +[world] +spawn_position = [ + -22.5, + 31.06250, + 21.5, +] +spawn_rotation = [ + -90.0, + 0.0, +] +dimension = "end" +time = "day" + +[world.experimental] +view_distance = 4 +schematic_file = "" +polar_file = "limbo.polar" +lock_time = false + +[world.boundaries] +enabled = false +min_y = -64 +teleport_message = "" + +[server_list] +reply_to_status = false +max_players = 20 +message_of_the_day = "Limbo" +show_online_player_count = false +server_icon = "server-icon.png" + +[compression] +threshold = -1 +level = 6 + +[tab_list] +enabled = false +header = "" +footer = "" +player_listed = false + +[boss_bar] +enabled = false +title = "" +health = 1.0 +color = "pink" +division = 0 + +[title] +enabled = false +title = "" +subtitle = "" +fade_in = 10 +stay = 70 +fade_out = 20 + +[commands] +spawn = "" +fly = "" +fly_speed = "" +transfer = "" diff --git a/configuration/settings.yml b/configuration/settings.yml deleted file mode 100644 index 8fbd8eeb5..000000000 --- a/configuration/settings.yml +++ /dev/null @@ -1,145 +0,0 @@ -# -# NanoLimbo configuration -# - -# Server's host address and port. Set ip empty to use public address -bind: - ip: '0.0.0.0' - port: 65535 - -# Max number of players can join to server -# Set -1 to make it infinite -maxPlayers: 100 - -# Server's data in servers list -ping: - description: '{"text": "&9NanoLimbo"}' - version: 'NanoLimbo' - # Return static protocol version number in ping result - # By default, its -1 to return the client version if it supported - # https://wiki.vg/Protocol_version_numbers - protocol: -1 - -# Available dimensions: OVERWORLD, NETHER, THE_END -dimension: THE_END - -# Whether to display the player in the player list -# For 1.16.5 clients, the player list will be sent even if disabled, to avoid crash -playerList: - enable: false - username: 'NanoLimbo' - -# Whether to display header and footer in player list -# For 1.8+ clients -headerAndFooter: - enable: false - header: '{"text": "&eWelcome!"}' - footer: '{"text": "&9NanoLimbo"}' - -# Setup player's game mode -# 0 - Survival -# 1 - Creative (hide HP and food bar) -# 2 - Adventure -# 3 - Spectator (hide all UI bars) -# Spectator works on 1.8+ clients -gameMode: 3 - -# Remove secure-chat toast -# For 1.20.5+ clients -secureProfile: false - -# Server name which is shown under F3 -# For 1.13+ clients -brandName: - enable: true - content: 'Hypixel' - -# Message sends when player joins to the server -joinMessage: - enable: false - text: '{"text": "&eWelcome to the Limbo!"}' - -# BossBar displays when player joins to the server -# For 1.9+ clients -bossBar: - enable: false - text: '{"text": "Welcome to the Limbo!"}' - health: 1.0 - # Available colors: PINK, BLUE, RED, GREEN, YELLOW, PURPLE, WHITE - color: PINK - # Available divisions: SOLID, DASHES_6, DASHES_10, DASHES_12, DASHES_20 - division: SOLID - -# Display title and subtitle -# For 1.8+ clients -title: - enable: false - # Set title text value empty, if you need only subtitle - title: '{"text": ""}' - # Set subtitle text value empty, if you need only title - subtitle: '{"text": ""}' - # Fade in time in ticks (1 sec = 20 ticks) - fadeIn: 10 - # Stay time in ticks - stay: 100 - # Fade out time in ticks - fadeOut: 10 - -# Player info forwarding support. -# Available types: -# - NONE -# - LEGACY -# - MODERN -# - BUNGEE_GUARD -# Don't use secret if you do not use MODERN type -infoForwarding: - type: MODERN - secret: 'ixmSUgWOgvs7' - tokens: - - '' - -# Read timeout for connections in milliseconds -readTimeout: 30000 - -# Define log level. For production, I'd recommend to use level 2 -# Log levels: -# 0 - Display only errors -# 1 - Display errors, warnings -# 2 - Display errors, warnings, info -# 3 - Display errors, warnings, info, debug -debugLevel: 2 - -# Warning! Do not touch params of this block if you are not completely sure what is this! -netty: - # Available transport type: - # - epoll - # - io_uring - # - kqueue - # - nio - transportType: epoll - # EventLoopGroup threads count - threads: - bossGroup: 1 - workerGroup: - -# Options to check incoming traffic and kick potentially malicious connections. -# Take into account that player can send many small packets, for example, just moving mouse. -traffic: - # If true, then additional handler will be added to the channel pipeline - enable: true - # Max packet size in bytes - # Unlimited if -1 - maxPacketSize: 8192 - # The interval to measure packets over - # Lowering this value will limit peak packets from players which would target people with bad connections - # Raising this value will allow higher peak packet rates, which will help with people who have poor connections - # Ignored if -1.0 - interval: 7.0 - # The maximum packets per second for players - # It is measured over the configured interval - # Ignored if -1.0 - maxPacketRate: 500.0 - # The maximum packet bytes per second for players - # It is measured over the configured interval as an average bytes/sec - # Ignored if -1.0 - maxPacketBytesRate: 2048.0 diff --git a/configuration/velocity.toml b/configuration/velocity.toml index e5d4e38de..7d2b81b95 100644 --- a/configuration/velocity.toml +++ b/configuration/velocity.toml @@ -42,7 +42,7 @@ forwarding-secret-file = "forwarding.secret" # Announce whether your server supports Forge. If you run a modded server, we # suggest turning this on. -# +# # If your network runs one modpack consistently, consider using ping-passthrough = "mods" # instead for a nicer display in the server list. announce-forge = false @@ -127,6 +127,11 @@ log-command-executions = false # and disconnecting from the proxy. log-player-connections = true +# How long (in milliseconds) should the proxy wait between disconnecting a player from one server +# and connecting them to another when switching servers? This can be useful to give servers time +# to properly clean up resources. +server-switch-delay = 100 + [query] # Whether to enable responding to GameSpy 4 query responses or not. enabled = false diff --git a/docker-compose.yml b/docker-compose.yml index 98e5ab068..78932a99b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,6 @@ +x-forwarding-env: &forwarding_env + FORWARDING_SECRET: ${FORWARDING_SECRET:-change-me} + services: mongodb: image: mongo:8.0.9 @@ -39,6 +42,8 @@ services: context: . dockerfile: "./DockerFiles/Dockerfile.proxy" container_name: hypixel_proxy + environment: + <<: *forwarding_env ports: - "25565:25565" depends_on: @@ -73,6 +78,7 @@ services: container_name: hypixelcore_island restart: "unless-stopped" environment: + <<: *forwarding_env SERVICE_CMD: java -jar HypixelCore.jar SKYBLOCK_ISLAND depends_on: proxy: @@ -89,6 +95,7 @@ services: container_name: hypixelcore_hub restart: "unless-stopped" environment: + <<: *forwarding_env SERVICE_CMD: java -jar HypixelCore.jar SKYBLOCK_HUB depends_on: proxy: @@ -105,6 +112,7 @@ services: container_name: hypixelcore_farming restart: "unless-stopped" environment: + <<: *forwarding_env SERVICE_CMD: java -jar HypixelCore.jar SKYBLOCK_THE_FARMING_ISLANDS depends_on: proxy: @@ -121,6 +129,7 @@ services: container_name: prototype_lobby restart: "unless-stopped" environment: + <<: *forwarding_env SERVICE_CMD: java -jar HypixelCore.jar PROTOTYPE_LOBBY depends_on: proxy: @@ -147,7 +156,7 @@ services: # - ./configuration:/app/configuration_files # networks: # - hypixel_network - + # bedwars_game: # image: game_server_prepared # container_name: bedwars_game @@ -164,12 +173,12 @@ services: # networks: # - hypixel_network - nanolimbo: - image: game_server_prepared - container_name: nanolimbo - restart: "unless-stopped" + pico-limbo: + image: ghcr.io/ariksquad/picolimbo:latest + container_name: picolimbo + restart: unless-stopped environment: - SERVICE_CMD: java -jar NanoLimbo-1.10.2.jar + <<: *forwarding_env depends_on: proxy: condition: service_healthy @@ -185,12 +194,13 @@ services: container_name: service_api restart: "unless-stopped" environment: + <<: *forwarding_env SERVICE_CMD: java -jar ServiceAPI.jar depends_on: proxy: condition: service_healthy game_server_builder: - condition: service_started + condition: service_started volumes: - ./configuration:/app/configuration_files networks: @@ -201,6 +211,7 @@ services: container_name: service_auctionhouse restart: "unless-stopped" environment: + <<: *forwarding_env SERVICE_CMD: java -jar ServiceAuctionHouse.jar depends_on: proxy: @@ -217,6 +228,7 @@ services: container_name: service_bazaar restart: "unless-stopped" environment: + <<: *forwarding_env SERVICE_CMD: java -jar ServiceBazaar.jar depends_on: proxy: @@ -233,6 +245,7 @@ services: container_name: service_itemtracker restart: "unless-stopped" environment: + <<: *forwarding_env SERVICE_CMD: java -jar ServiceItemTracker.jar depends_on: proxy: @@ -249,6 +262,7 @@ services: container_name: service_party restart: "unless-stopped" environment: + <<: *forwarding_env SERVICE_CMD: java -jar ServiceParty.jar depends_on: proxy: diff --git a/loader/src/main/java/net/swofty/loader/Hypixel.java b/loader/src/main/java/net/swofty/loader/Hypixel.java index d8b8ab034..94365de2b 100644 --- a/loader/src/main/java/net/swofty/loader/Hypixel.java +++ b/loader/src/main/java/net/swofty/loader/Hypixel.java @@ -38,13 +38,18 @@ import org.reflections.Reflections; import org.tinylog.Logger; +import java.net.InetAddress; import java.nio.file.Files; -import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import java.net.InetAddress; public class Hypixel { @@ -63,7 +68,7 @@ static void main(String[] args) { return; } - if (ConfigProvider.settings().getIntegrations().getSentryDsn().isBlank()) { + if (!ConfigProvider.settings().getIntegrations().getSentryDsn().isBlank()) { Sentry.init(options -> { options.setDsn(ConfigProvider.settings().getIntegrations().getSentryDsn()); options.setSendDefaultPii(true); diff --git a/server/proxy/velocity.jar b/server/proxy/velocity.jar index 39471c14f..09a0a0c1d 100644 Binary files a/server/proxy/velocity.jar and b/server/proxy/velocity.jar differ diff --git a/service.punishment/build.gradle.kts b/service.punishment/build.gradle.kts index c2cd546d3..b01a4ff58 100644 --- a/service.punishment/build.gradle.kts +++ b/service.punishment/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { implementation("org.mongodb:mongodb-driver-sync:5.6.2") //implementation("com.github.Swofty-Developments:AtlasRedisAPI:1.1.3") - implementation("redis.clients:jedis:4.2.3") + implementation("redis.clients:jedis:7.2.0") } application { diff --git a/setup/install.sh b/setup/install.sh index d57936fa1..bd69555ec 100644 --- a/setup/install.sh +++ b/setup/install.sh @@ -35,6 +35,43 @@ done # ─── Global error trap (safety net for uncaught errors) ────────────────────── trap 'on_error ${LINENO}' ERR +generate_forwarding_secret() { + if command -v openssl >/dev/null 2>&1; then + openssl rand -hex 24 + else + cat /proc/sys/kernel/random/uuid | tr -d '-' + fi +} + +setup_forwarding_secret() { + local env_file="${INSTALL_DIR}/.env" + local secret="" + + if [[ -f "$env_file" ]]; then + secret=$(grep -E '^FORWARDING_SECRET=' "$env_file" | tail -n1 | cut -d'=' -f2- || true) + fi + + if [[ -z "$secret" && -f "${INSTALL_DIR}/configuration/forwarding.secret" ]]; then + secret=$(cat "${INSTALL_DIR}/configuration/forwarding.secret") + fi + + if [[ -z "$secret" ]]; then + secret=$(generate_forwarding_secret) + fi + + mkdir -p "$INSTALL_DIR" "${INSTALL_DIR}/configuration" + + { + [[ -f "$env_file" ]] && grep -vE '^FORWARDING_SECRET=' "$env_file" || true + echo "FORWARDING_SECRET=${secret}" + } > "${env_file}.tmp" + mv "${env_file}.tmp" "$env_file" + + printf '%s' "$secret" > "${INSTALL_DIR}/configuration/forwarding.secret" + export FORWARDING_SECRET="$secret" + log_ok "Forwarding secret configured" +} + # ─── Re-run detection ──────────────────────────────────────────────────────── handle_existing_install() { local state_file="${1}/${STATE_FILE}" @@ -63,6 +100,9 @@ main_install() { set_stage "Cleanup" handle_existing_install "$INSTALL_DIR" + set_stage "Secrets" + setup_forwarding_secret + set_stage "Setup" do_setup diff --git a/setup/lib/docker.sh b/setup/lib/docker.sh index 16c3e2c2e..57713da8b 100644 --- a/setup/lib/docker.sh +++ b/setup/lib/docker.sh @@ -11,7 +11,7 @@ do_launch() { set_step "Cleaning up stale containers" log_info "Removing stale containers..." - for c in $(docker ps -a --format '{{.Names}}' 2>/dev/null | grep -E '^(hypixel_|hypixelcore_|service_|nanolimbo|game_server_builder)'); do + for c in $(docker ps -a --format '{{.Names}}' 2>/dev/null | grep -E '^(hypixel_|hypixelcore_|service_|pico-limbo|game_server_builder)'); do docker rm -f "$c" &>/dev/null || true done docker volume rm mongo-data &>/dev/null || true @@ -48,11 +48,11 @@ do_launch() { fi log_ok "Proxy healthy" - set_step "Starting NanoLimbo" - log_info "Starting NanoLimbo + image builder..." - run_step "Starting NanoLimbo + builder" docker compose up -d game_server_builder nanolimbo + set_step "Starting PicoLimbo" + log_info "Starting PicoLimbo + image builder..." + run_step "Starting PicoLimbo + builder" docker compose up -d game_server_builder pico-limbo sleep 3 - log_ok "NanoLimbo started" + log_ok "PicoLimbo started" if [[ ${#SELECTED_SERVICES[@]} -gt 0 ]]; then set_step "Starting services" @@ -254,7 +254,7 @@ start_all() { docker compose up -d proxy 2>&1 || true wait_healthy "hypixel_proxy" 60 || log_warn "Proxy may not be healthy" - docker compose up -d game_server_builder nanolimbo 2>&1 || true + docker compose up -d game_server_builder pico-limbo 2>&1 || true sleep 2 docker compose up -d 2>&1 || true diff --git a/setup/lib/setup.sh b/setup/lib/setup.sh index d0027db09..8362d914a 100644 --- a/setup/lib/setup.sh +++ b/setup/lib/setup.sh @@ -83,7 +83,7 @@ integrations: via-version: false sentry-dsn: '' limbo: - host-name: nanolimbo + host-name: picolimbo port: 65535 resource-pack: public-url: '' @@ -115,7 +115,7 @@ generate_dockerfiles() { cat > "${INSTALL_DIR}/DockerFiles/Dockerfile.proxy" <<'PROXY_EOF' FROM eclipse-temurin:25-jdk WORKDIR /app -RUN apt-get update && apt-get install -y jq expect netcat-traditional curl && apt-get clean +RUN apt-get update && apt-get install -y jq netcat-traditional curl && apt-get clean PROXY_EOF cat >> "${INSTALL_DIR}/DockerFiles/Dockerfile.proxy" <> "${INSTALL_DIR}/DockerFiles/Dockerfile.proxy" <<'PROXY_EOF' COPY ./configuration /app/configuration_files -RUN printf '#!/usr/bin/expect -f\nset timeout 120\nspawn java -jar velocity.jar\nexpect ">"\nsend "shutdown\\r"\nexpect eof\n' > /tmp/run_velocity.exp && \ - chmod +x /tmp/run_velocity.exp && \ - /tmp/run_velocity.exp && \ - rm /tmp/run_velocity.exp RUN rm -f velocity.toml && \ cp configuration_files/velocity.toml velocity.toml RUN mkdir -p configuration && \ - cp configuration_files/config.example.yml ./configuration/config.yml && \ - secret=$(cat forwarding.secret) && \ - sed -i "s/velocity-secret: .*/velocity-secret: '$secret'/" ./configuration/config.yml + cp configuration_files/config.example.yml ./configuration/config.yml EXPOSE 25565 -CMD ["sh", "-c", "cp forwarding.secret /app/configuration_files/forwarding.secret && java -jar velocity.jar"] +CMD ["sh", "-c", "[ -n \"$FORWARDING_SECRET\" ] || { echo 'FORWARDING_SECRET is required' >&2; exit 1; }; printf '%s' \"$FORWARDING_SECRET\" > /app/forwarding.secret; sed -i \"s/velocity-secret: .*/velocity-secret: '$FORWARDING_SECRET'/\" /app/configuration/config.yml; java -jar velocity.jar"] PROXY_EOF local jar_downloads="" @@ -166,12 +160,10 @@ RUN curl -fSL -o /tmp/worlds.tar.gz "${worlds_url}" && \\ tar -xzf /tmp/worlds.tar.gz -C ./configuration && \\ rm /tmp/worlds.tar.gz EXPOSE 25565 65535 8080 20000 -RUN cp configuration_files/NanoLimbo-1.10.2.jar ./NanoLimbo-1.10.2.jar && \\ - cp configuration_files/settings.yml ./settings.yml && \\ +RUN cp configuration_files/server.toml ./server.toml && \\ cp -a configuration_files/skyblock/. configuration/skyblock/ && \\ cp configuration_files/entrypoint.sh ./entrypoint.sh && \\ - chmod +x entrypoint.sh && \\ - sed -i "s/ip: 'localhost'/ip: '0.0.0.0'/" ./settings.yml + chmod +x entrypoint.sh CMD ["sh", "entrypoint.sh"] GAME_EOF @@ -191,6 +183,8 @@ generate_docker_compose() { fi cat > "$compose_file" <) permissionsEvent -> EventTask.withContinuation(continuation -> { - permissionsEvent.setProvider(permissionSubject -> PermissionFunction.ALWAYS_FALSE); + permissionsEvent.setProvider(_ -> PermissionFunction.ALWAYS_FALSE); continuation.resume(); })); server.getEventManager().register(this, DisconnectEvent.class, PostOrder.LAST, @@ -170,9 +174,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) { }); }).repeat(Duration.ofSeconds(10)).schedule(); - /** - * Register commands - */ + // Register commands CommandManager commandManager = proxy.getCommandManager(); CommandMeta statusCommandMeta = commandManager.metaBuilder("serverstatus") .aliases("status") @@ -188,6 +190,11 @@ public void onProxyInitialization(ProxyInitializeEvent event) { commandManager.register(protocolVersionMeta, new ProtocolVersionCommand()); + CommandMeta limboCommandMeta = commandManager.metaBuilder("limbo") + .plugin(this) + .build(); + + commandManager.register(limboCommandMeta, new LimboCommand()); // Handle database new ProfilesDatabase("_placeHolder").connect(ConfigProvider.settings().getMongodb()); @@ -212,9 +219,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) { .forEach(ServerOutboundMessage::registerFromProtocolObject); RedisAPI.getInstance().startListeners(); - /** - * Setup GameManager - */ + // Setup GameManager GameManager.loopServers(server); } @@ -338,7 +343,7 @@ public void onServerCrash(KickedFromServerEvent event) { )); TransferHandler transferHandler = new TransferHandler(event.getPlayer()); - transferHandler.standardTransferTo(originalServer, serverType); + transferHandler.transferTo(serverType); CompletableFuture.delayedExecutor(GameManager.SLEEP_TIME + 300, TimeUnit.MILLISECONDS) .execute(() -> { @@ -366,7 +371,7 @@ public void onServerCrash(KickedFromServerEvent event) { event.getPlayer().disconnect(reason); return; } - transferHandler.noLimboTransferTo(server.registeredServer()); + transferHandler.transferTo(server.registeredServer()); if (!serverTypeToTry.isSkyBlock()) { event.getPlayer().sendPlainMessage("§cAn exception occurred in your connection, so you were put into the Prototype Lobby."); @@ -394,18 +399,26 @@ public void onPing(ProxyPingEvent event) { @Subscribe public void onPlayerConnect(ServerPostConnectEvent event) { - if (!(event.getPlayer().getProtocolVersion().getProtocol() >= ProtocolVersion.MAXIMUM_VERSION.getProtocol())) { + Player player = event.getPlayer(); + if (!(player.getProtocolVersion().getProtocol() >= ProtocolVersion.MAXIMUM_VERSION.getProtocol())) { StringBuilder message = new StringBuilder(); message.append("\n"); message.append("§6§l----------- §cServer Notice §6§l-----------\n"); - message.append("§cAlthough we do support versions prior to §6" + ProtocolVersion.MAXIMUM_VERSION.getVersionIntroducedIn() + "§c, the experience may be buggy.\n"); - message.append("§cIf you experience a bug, please test if it also occurs on §6" + ProtocolVersion.MAXIMUM_VERSION.getVersionIntroducedIn() + "§c before reporting it.\n"); + message.append("§cAlthough we do support versions prior to §6" + ProtocolVersion.MAXIMUM_VERSION.getVersionIntroducedIn() + "§c, the experience may be degraded.\n"); + message.append("§cIf you experience any issues, please test if it also occurs on §6" + ProtocolVersion.MAXIMUM_VERSION.getVersionIntroducedIn() + "§c before reporting it.\n"); message.append("§6§l---------------------------------\n"); message.append("\n"); - event.getPlayer().sendMessage(Component.text(message.toString())); + player.sendMessage(Component.text(message.toString())); } + + player.getCurrentServer().ifPresent(connection -> { + if (connection.getServer() == limboServer) { + player.sendMessage(Component.text("§cYou were spawned in Limbo.")); + player.sendMessage(Component.text("§b/limbo for more information.")); + } + }); } public static Stream loopThroughPackage(String packageName, Class clazz) { @@ -421,7 +434,7 @@ public static Stream loopThroughPackage(String packageName, Class claz return null; } }) - .filter(java.util.Objects::nonNull); + .filter(Objects::nonNull); } private void injectPlayer(Player player) { diff --git a/velocity.extension/src/main/java/net/swofty/velocity/command/LimboCommand.java b/velocity.extension/src/main/java/net/swofty/velocity/command/LimboCommand.java new file mode 100644 index 000000000..68ad4c38d --- /dev/null +++ b/velocity.extension/src/main/java/net/swofty/velocity/command/LimboCommand.java @@ -0,0 +1,36 @@ +package net.swofty.velocity.command; + +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.command.SimpleCommand; +import com.velocitypowered.api.proxy.Player; +import net.kyori.adventure.text.Component; +import net.swofty.velocity.SkyBlockVelocity; +import net.swofty.velocity.gamemanager.TransferHandler; + +public class LimboCommand implements SimpleCommand { + + @Override + public void execute(Invocation invocation) { + CommandSource source = invocation.source(); + if (!(source instanceof Player player)) { + return; + } + player.getCurrentServer().ifPresent((connection) -> { + if (connection.getServer() == SkyBlockVelocity.getLimboServer()) { + for (int i = 0; i < 5; i++) { + player.sendMessage(Component.empty()); + } + player.sendMessage(Component.text("§cThe lobby you attempted to join was full or offline.")); + player.sendMessage(Component.text("§eBecause of this, you were routed to Limbo, a subset of your own imagination.")); + player.sendMessage(Component.text("§dThis place doesn't exist anywhere, any you can stay here as long as you'd like.")); + player.sendMessage(Component.text("§6To return to \"reality\", use §b/lobby GAME.")); + player.sendMessage(Component.text("§cExamples: /lobby, /lobby skywars, /lobby arcade")); + player.sendMessage(Component.text("§4Watch out, though, as there are things that live in Limbo.")); + return; + } + + TransferHandler transferHandler = new TransferHandler(player); + transferHandler.sendToLimbo(); + }); + } +} diff --git a/velocity.extension/src/main/java/net/swofty/velocity/gamemanager/TransferHandler.java b/velocity.extension/src/main/java/net/swofty/velocity/gamemanager/TransferHandler.java index fcbdec429..b1af68836 100644 --- a/velocity.extension/src/main/java/net/swofty/velocity/gamemanager/TransferHandler.java +++ b/velocity.extension/src/main/java/net/swofty/velocity/gamemanager/TransferHandler.java @@ -4,13 +4,16 @@ import com.velocitypowered.api.proxy.server.RegisteredServer; import net.kyori.adventure.text.Component; import net.swofty.commons.ServerType; -import net.swofty.commons.config.ConfigProvider; import net.swofty.commons.proxy.FromProxyChannels; import net.swofty.velocity.SkyBlockVelocity; import net.swofty.velocity.redis.RedisMessage; import org.json.JSONObject; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; import java.util.concurrent.CompletableFuture; public record TransferHandler(Player player) { @@ -30,29 +33,6 @@ public void removeFromDisregard() { disregard.remove(player); } - public void standardTransferTo(RegisteredServer currentServer, ServerType type) { - if (type == null) { - player.disconnect(Component.text("§cWe encountered an error while trying to transfer you to a server. Please try again later.")); - return; - } - - new Thread(() -> { - boolean hasEmptyServer = GameManager.hasType(type) && GameManager.isAnyEmpty(type); - if (!hasEmptyServer) { - player.sendMessage(Component.text( - "§cThere are no SkyBlock (type=" + type.name() + ") servers available at the moment." - )); - return; - } - - RegisteredServer limboServer = SkyBlockVelocity.getLimboServer(); - - player.createConnectionRequest(limboServer).connectWithIndication(); - playersGoalServerType.put(player, type); - playersOriginServer.put(player, currentServer); - }).start(); - } - public CompletableFuture sendToLimbo() { CompletableFuture future = new CompletableFuture<>(); @@ -125,12 +105,6 @@ public void previousServerIsFinished() { playersOriginServer.remove(player); playersGoalServerType.remove(player); - try { - Thread.sleep(ConfigProvider.settings().getTransferTimeout()); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - player.sendMessage(Component.text("§7Sending to server " + server.displayName() + "...")); player.createConnectionRequest(server.registeredServer()).connectWithIndication(); @@ -140,9 +114,13 @@ public void previousServerIsFinished() { }).start(); } - public void noLimboTransferTo(ServerType type) { + public void transferTo(ServerType type) { new Thread(() -> { RegisteredServer originServer = playersOriginServer.get(player); + if (originServer == null) { + player.getCurrentServer().ifPresent(conn -> playersOriginServer.put(player, conn.getServer())); + originServer = playersOriginServer.get(player); + } ServerType originServerType = GameManager.getTypeFromRegisteredServer(originServer); playersGoalServerType.remove(player); @@ -173,27 +151,36 @@ public void forceRemoveFromLimbo() { playersOriginServer.remove(player); } - public void noLimboTransferTo(RegisteredServer toTransferTo) { + public CompletableFuture transferTo(RegisteredServer toTransferTo) { + CompletableFuture future = new CompletableFuture<>(); new Thread(() -> { - RegisteredServer originServer = playersOriginServer.get(player); - ServerType originServerType = GameManager.getTypeFromRegisteredServer(originServer); - - playersGoalServerType.remove(player); - playersOriginServer.remove(player); - - ServerType type = GameManager.getTypeFromRegisteredServer(toTransferTo); - UUID serverUUID = UUID.fromString(toTransferTo.getServerInfo().getName()); - - if (originServer != null && originServerType != null) { - RedisMessage.sendMessageToServer(serverUUID, - FromProxyChannels.GIVE_PLAYERS_ORIGIN_TYPE, - new JSONObject().put("uuid", player.getUniqueId().toString()) - .put("origin-type", originServerType.name()) - ); + try { + RegisteredServer originServer = playersOriginServer.get(player); + if (originServer == null) { + player.getCurrentServer().ifPresent(conn -> playersOriginServer.put(player, conn.getServer())); + originServer = playersOriginServer.get(player); + } + ServerType originServerType = GameManager.getTypeFromRegisteredServer(originServer); + + playersGoalServerType.remove(player); + playersOriginServer.remove(player); + + UUID serverUUID = UUID.fromString(toTransferTo.getServerInfo().getName()); + + if (originServer != null && originServerType != null) { + RedisMessage.sendMessageToServer(serverUUID, + FromProxyChannels.GIVE_PLAYERS_ORIGIN_TYPE, + new JSONObject().put("uuid", player.getUniqueId().toString()) + .put("origin-type", originServerType.name()) + ); + } + + player.createConnectionRequest(toTransferTo).connectWithIndication(); + future.complete(null); + } catch (Exception e) { + future.completeExceptionally(e); } - - GameManager.GameServer toTransferToAsGame = GameManager.getFromUUID(serverUUID); - player.createConnectionRequest(toTransferTo).connectWithIndication(); }).start(); + return future; } } diff --git a/velocity.extension/src/main/java/net/swofty/velocity/redis/listeners/ListenerPlayerHandler.java b/velocity.extension/src/main/java/net/swofty/velocity/redis/listeners/ListenerPlayerHandler.java index 1fc05bb28..d8e1c54b9 100644 --- a/velocity.extension/src/main/java/net/swofty/velocity/redis/listeners/ListenerPlayerHandler.java +++ b/velocity.extension/src/main/java/net/swofty/velocity/redis/listeners/ListenerPlayerHandler.java @@ -7,7 +7,6 @@ import net.swofty.commons.ServerType; import net.swofty.commons.StringUtility; import net.swofty.commons.UnderstandableProxyServer; -import net.swofty.commons.config.ConfigProvider; import net.swofty.commons.proxy.FromProxyChannels; import net.swofty.commons.proxy.ToProxyChannels; import net.swofty.commons.proxy.requirements.to.PlayerHandlerRequirements; @@ -22,19 +21,16 @@ import java.util.Optional; import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; @ChannelListener(channel = ToProxyChannels.PLAYER_HANDLER) public class ListenerPlayerHandler extends RedisListener { - private static final java.util.Map bedwarsPreferences = new java.util.concurrent.ConcurrentHashMap<>(); - @Override - public JSONObject receivedMessage(JSONObject message, UUID serverUUID) { - UUID uuid = UUID.fromString(message.getString("uuid")); - PlayerHandlerRequirements.PlayerHandlerActions action = - PlayerHandlerRequirements.PlayerHandlerActions.valueOf( - message.getString("action")); + @Override + public JSONObject receivedMessage(JSONObject message, UUID serverUUID) { + UUID uuid = UUID.fromString(message.getString("uuid")); + PlayerHandlerRequirements.PlayerHandlerActions action = + PlayerHandlerRequirements.PlayerHandlerActions.valueOf( + message.getString("action")); Optional potentialPlayer = SkyBlockVelocity.getServer().getPlayer(uuid); if (potentialPlayer.isEmpty()) { @@ -61,13 +57,13 @@ public JSONObject receivedMessage(JSONObject message, UUID serverUUID) { publishPresence(player, true); return new JSONObject().put("server", new UnderstandableProxyServer( - serverInfo.displayName(), - serverInfo.internalID(), - GameManager.getTypeFromRegisteredServer(serverInfo.registeredServer()), - serverInfo.registeredServer().getServerInfo().getAddress().getPort(), - serverInfo.registeredServer().getPlayersConnected().stream().map(Player::getUniqueId).toList(), - serverInfo.maxPlayers(), - serverInfo.shortDisplayName() + serverInfo.displayName(), + serverInfo.internalID(), + GameManager.getTypeFromRegisteredServer(serverInfo.registeredServer()), + serverInfo.registeredServer().getServerInfo().getAddress().getPort(), + serverInfo.registeredServer().getPlayersConnected().stream().map(Player::getUniqueId).toList(), + serverInfo.maxPlayers(), + serverInfo.shortDisplayName() ).toJSON()); } case TRANSFER_WITH_UUID -> { @@ -78,7 +74,7 @@ public JSONObject receivedMessage(JSONObject message, UUID serverUUID) { if (serverInfo == null) { player.sendMessage(Component.text( - "§cWe encountered an issue while attempting to locate the server on the network. Please try again later." + "§cWe encountered an issue while attempting to locate the server on the network. Please try again later." )); return new JSONObject(); } @@ -87,76 +83,63 @@ public JSONObject receivedMessage(JSONObject message, UUID serverUUID) { if (!serverInfo.hasEmptySlots()) { player.sendMessage(Component.text( - "§cAttempted to connect to " + serverInfo.displayName() + ", but there are no empty slots available. Please try again later." + "§cAttempted to connect to " + serverInfo.displayName() + ", but there are no empty slots available. Please try again later." )); return new JSONObject(); } transferHandler.addToDisregard(); - transferHandler.sendToLimbo().join(); + transferHandler.transferTo(serverInfo.registeredServer()) + .thenRun(transferHandler::removeFromDisregard); - // Trick the packet blocker into thinking player is in normal transfer process - TransferHandler.playersGoalServerType.put(player, ServerType.SKYBLOCK_HUB); - - CompletableFuture.delayedExecutor(ConfigProvider.settings().getTransferTimeout(), TimeUnit.MILLISECONDS) - .execute(() -> { - TransferHandler.playersGoalServerType.remove(player); - transferHandler.noLimboTransferTo(serverInfo.registeredServer()); - transferHandler.removeFromDisregard(); - }); } - case TRANSFER -> { - ServerType type = ServerType.valueOf(message.getString("type")); - if (!GameManager.hasType(type) - || new TransferHandler(player).isInLimbo() - || !GameManager.isAnyEmpty(type)) { - player.sendMessage(Component.text( - "§cAttempted to transfer to a " + StringUtility.toNormalCase(type.name()) + " server, but there are no empty slots available. Please try again later." - )); - return new JSONObject(); - } - new TransferHandler(player).standardTransferTo( - player.getCurrentServer().get().getServer(), - type - ); - } - case LIMBO -> { - new TransferHandler(player).sendToLimbo().join(); - } - case TELEPORT -> { - if (potentialServer.isEmpty()) { - return new JSONObject(); - } - UUID server = UUID.fromString(potentialServer.get().getServer().getServerInfo().getName()); - return RedisMessage.sendMessageToServer(server, - FromProxyChannels.TELEPORT, - message).join(); - } - case EVENT -> { - if (potentialServer.isEmpty()) { - return new JSONObject(); - } - UUID server = UUID.fromString(potentialServer.get().getServer().getServerInfo().getName()); - RedisMessage.sendMessageToServer(server, - FromProxyChannels.RUN_EVENT_ON_SERVER, - message - ).join(); - } - case REFRESH_COOP_DATA -> { - if (potentialServer.isEmpty()) { - return new JSONObject(); - } - UUID server = UUID.fromString(potentialServer.get().getServer().getServerInfo().getName()); - RedisMessage.sendMessageToServer(server, - FromProxyChannels.REFRESH_COOP_DATA_ON_SERVER, - message - ).join(); - } - case MESSAGE -> { - String messageToSend = message.getString("message"); - player.sendMessage(JSONComponentSerializer.json().deserialize(messageToSend)); - } - } + case TRANSFER -> { + ServerType type = ServerType.valueOf(message.getString("type")); + if (!GameManager.hasType(type) + || new TransferHandler(player).isInLimbo() + || !GameManager.isAnyEmpty(type)) { + player.sendMessage(Component.text( + "§cAttempted to transfer to a " + StringUtility.toNormalCase(type.name()) + " server, but there are no empty slots available. Please try again later." + )); + return new JSONObject(); + } + new TransferHandler(player).transferTo(type); + } + case LIMBO -> new TransferHandler(player).sendToLimbo().join(); + case TELEPORT -> { + if (potentialServer.isEmpty()) { + return new JSONObject(); + } + UUID server = UUID.fromString(potentialServer.get().getServer().getServerInfo().getName()); + return RedisMessage.sendMessageToServer(server, + FromProxyChannels.TELEPORT, + message).join(); + } + case EVENT -> { + if (potentialServer.isEmpty()) { + return new JSONObject(); + } + UUID server = UUID.fromString(potentialServer.get().getServer().getServerInfo().getName()); + RedisMessage.sendMessageToServer(server, + FromProxyChannels.RUN_EVENT_ON_SERVER, + message + ).join(); + } + case REFRESH_COOP_DATA -> { + if (potentialServer.isEmpty()) { + return new JSONObject(); + } + UUID server = UUID.fromString(potentialServer.get().getServer().getServerInfo().getName()); + RedisMessage.sendMessageToServer(server, + FromProxyChannels.REFRESH_COOP_DATA_ON_SERVER, + message + ).join(); + } + case MESSAGE -> { + String messageToSend = message.getString("message"); + player.sendMessage(JSONComponentSerializer.json().deserialize(messageToSend)); + } + } return new JSONObject(); } @@ -165,7 +148,7 @@ private void publishPresence(Player player, boolean online) { Optional serverConn = player.getCurrentServer(); var type = serverConn.map(conn -> GameManager.getTypeFromRegisteredServer(conn.getServer())).orElse(null); PresencePublisher.publish(player, online, serverConn.map(ServerConnection::getServer).orElse(null), - type != null ? type.name() : null); + type != null ? type.name() : null); } catch (Exception ignored) { } } diff --git a/velocity.extension/velocity-proxy-3.4.0-SNAPSHOT.jar b/velocity.extension/velocity-3.5.0-SNAPSHOT-576.jar similarity index 64% rename from velocity.extension/velocity-proxy-3.4.0-SNAPSHOT.jar rename to velocity.extension/velocity-3.5.0-SNAPSHOT-576.jar index 0b77b40cd..83d71cccf 100644 Binary files a/velocity.extension/velocity-proxy-3.4.0-SNAPSHOT.jar and b/velocity.extension/velocity-3.5.0-SNAPSHOT-576.jar differ diff --git a/website/docs/docker/setup.md b/website/docs/docker/setup.md index c5d0dabae..70f869ec7 100644 --- a/website/docs/docker/setup.md +++ b/website/docs/docker/setup.md @@ -9,6 +9,7 @@ curl -fsSL skyblock-installer.swofty.net | bash ``` The installer will: + 1. Check and install dependencies (Docker, `gum`, `figlet`) 2. Run a system requirements check 3. Let you pick which server types and services to run @@ -24,13 +25,13 @@ Requires **Linux** with **Docker** installed. The installer will guide you throu The installer walks you through: -| Step | What It Does | -|------|-------------| -| System Check | Validates RAM, CPU, disk space, Docker version | -| Configuration | Pick install directory, bind IP, online mode | -| Server Selection | Choose from 14 SkyBlock servers and 10 minigame servers | -| Service Selection | Pick which microservices to run (DataMutex and Party are required) | -| Build & Launch | Builds Docker images, starts containers in order, waits for health checks | +| Step | What It Does | +|-------------------|---------------------------------------------------------------------------| +| System Check | Validates RAM, CPU, disk space, Docker version | +| Configuration | Pick install directory, bind IP, online mode | +| Server Selection | Choose from 14 SkyBlock servers and 10 minigame servers | +| Service Selection | Pick which microservices to run (DataMutex and Party are required) | +| Build & Launch | Builds Docker images, starts containers in order, waits for health checks | ### Management Dashboard @@ -78,6 +79,20 @@ In your `configuration` folder: 1. Remove the default `config.yml` 2. Rename `config.docker.yml` to `config.yml` +In the top of `docker-compose.yml` change the `change-me` to other. + +```yml +x-forwarding-env: &forwarding_env + FORWARDING_SECRET: ${FORWARDING_SECRET:-change-me} +``` + +For reference: + +```yml +x-forwarding-env: &forwarding_env + FORWARDING_SECRET: ${FORWARDING_SECRET:-i7sC4xqh} +``` + ### 3. Build and Run ```bash @@ -100,14 +115,14 @@ docker compose down The Docker Compose setup starts: -| Container | Purpose | -|-----------|---------| -| MongoDB | Database | -| Redis | Caching & messaging | -| Velocity Proxy | Player connections | -| NanoLimbo | Connection queue | -| Game Servers | Gameplay instances | -| Services | Microservices (API, Auctions, Bazaar, etc.) | +| Container | Purpose | +|----------------|---------------------------------------------| +| MongoDB | Database | +| Redis | Caching & messaging | +| Velocity Proxy | Player connections | +| PicoLimbo | Connection queue | +| Game Servers | Gameplay instances | +| Services | Microservices (API, Auctions, Bazaar, etc.) | ## Connecting @@ -120,11 +135,13 @@ localhost:25565 ## Logs and Debugging View logs for all containers: + ```bash docker compose logs -f ``` View logs for a specific container: + ```bash docker compose logs -f hypixel_proxy docker compose logs -f hypixelcore_skyblock_hub diff --git a/website/docs/reference/configuration.md b/website/docs/reference/configuration.md index 87c94bd56..bf1b3dbc3 100644 --- a/website/docs/reference/configuration.md +++ b/website/docs/reference/configuration.md @@ -20,18 +20,18 @@ spark: false anticheat: false redis-uri: redis://localhost:6379 limbo: - host-name: 127.0.0.1 - port: 65535 + host-name: 127.0.0.1 + port: 65535 ``` ### Fields -| Field | Required | Description | -|----------------------|----------|----------------------------------------------| -| `velocity-secret` | Yes | Must match `forwarding.secret` from Velocity | -| `mongodb-uri` | Yes | MongoDB connection string | -| `redis-uri` | Yes | Redis connection string | +| Field | Required | Description | +|-------------------|----------|----------------------------------------------| +| `velocity-secret` | Yes | Must match `forwarding.secret` from Velocity | +| `mongodb-uri` | Yes | MongoDB connection string | +| `redis-uri` | Yes | Redis connection string | ### Docker Configuration @@ -75,24 +75,18 @@ motd = "HypixelSkyBlock" Servers register dynamically via Redis, so you don't need to manually configure server entries. -## NanoLimbo settings.yml +## PicoLimbo server.yml Configuration for the limbo server. -**Location**: `settings.yml` (NanoLimbo directory) +**Location**: `server.toml` (PicoLimbo directory) ### Key Settings -```yaml -# Bind settings -bind: - host: "0.0.0.0" - port: 25566 - -# Velocity forwarding -forwarding: - type: MODERN - secret: "your-velocity-secret" +```toml +[forwarding] +method = "MODERN" +secret = "${FORWARDING_SECRET}" ``` :::alert warning @@ -102,6 +96,7 @@ The `secret` must match your Velocity forwarding secret exactly. ## Directory Structure ### Proxy Directory + ``` proxy/ ├── velocity-3.4.0-SNAPSHOT-528.jar @@ -114,6 +109,7 @@ proxy/ ``` ### Game Server Directory + ``` gameserver/ ├── HypixelCore.jar @@ -130,6 +126,7 @@ gameserver/ ``` ### Service Directory + ``` services/ ├── ServiceAPI.jar diff --git a/website/docs/setup/game-servers.md b/website/docs/setup/game-servers.md index 4259603b1..ab811721e 100644 --- a/website/docs/setup/game-servers.md +++ b/website/docs/setup/game-servers.md @@ -7,7 +7,7 @@ Game servers run the actual gameplay using Minestom. Each server runs as a speci 1. Download `HypixelCore.jar` from the [releases page](https://github.com/Swofty-Developments/HypixelSkyBlock/releases/tag/latest) 2. Download [`config.yml`](https://github.com/Swofty-Developments/HypixelSkyBlock/tree/master/configuration) 3. Download [world files](https://files.catbox.moe/of7snu.zip) -4. Download [`NanoLimbo.jar`](https://github.com/Swofty-Developments/HypixelSkyBlock/tree/master/configuration) and its config +4. Download [`PicoLimbo.jar`](https://github.com/Swofty-Developments/HypixelSkyBlock/tree/master/configuration) and its config ## Directory Structure @@ -78,15 +78,15 @@ Download from [configuration/skyblock](https://github.com/Swofty-Developments/Hy - `collections/` folder → `configuration/skyblock/collections/` - `songs/` folder → `configuration/skyblock/songs/` (optional) -### 5. Setup NanoLimbo +### 5. Setup PicoLimbo -NanoLimbo handles players during server transfers: +PicoLimbo handles the limbo state: -1. Place `NanoLimbo-1.10.2.jar` in a separate directory -2. Run it once: `java -jar NanoLimbo-1.10.2.jar` -3. Edit generated `settings.yml`: - - Set `type: MODERN` - - Set `secret: 'YOUR_VELOCITY_SECRET'` +1. Place `PicoLimbo.jar` in a separate directory +2. Edit the `server.toml` which you can find in the repository configuration folder: + - Set `method="MODERN"` + - Set `secret="YOUR_SECRET"` (or FORWARDING_SECRET environment variable by default) +3. Move the `limbo.polar` file to the same directory, or set polar_file to empty: `polar_file=""` 4. Keep it running in the background ### 6. Start a Game Server diff --git a/website/docs/troubleshooting.md b/website/docs/troubleshooting.md index 4b56f67c0..1aab958d4 100644 --- a/website/docs/troubleshooting.md +++ b/website/docs/troubleshooting.md @@ -6,9 +6,11 @@ Common issues and their solutions. ### Redis Connection Failed -**Error**: `redis.clients.jedis.exceptions.JedisConnectionException: Failed to connect to any host resolved for DNS name.` +**Error**: +`redis.clients.jedis.exceptions.JedisConnectionException: Failed to connect to any host resolved for DNS name.` **Solutions**: + 1. Verify Redis/Memurai is running 2. Check the `redis-uri` in `config.yml` 3. On Windows, try [this Redis port](https://github.com/tporadowski/redis/releases) instead of Memurai @@ -18,17 +20,19 @@ Common issues and their solutions. **Symptoms**: Client times out or refuses connection **Checklist**: + 1. Is Velocity proxy running? 2. Is at least one game server running? -3. Is NanoLimbo running? +3. Is PicoLimbo running? 4. Check if the `velocity-secret` matches everywhere: - - `forwarding.secret` (Velocity) - - `config.yml` (game servers) - - `settings.yml` (NanoLimbo) + - `forwarding.secret` (Velocity) + - `config.yml` (game servers) + - `server.toml` (PicoLimbo) ### MongoDB Connection Failed **Solutions**: + 1. Verify MongoDB is running on port 27017 2. Check `mongodb-uri` in `config.yml` 3. If using authentication, include credentials in URI: @@ -43,15 +47,26 @@ Common issues and their solutions. **Cause**: Regions not imported correctly **Solution**: + 1. Download `Minestom.regions.csv` from the configuration folder 2. Import it to the `regions` collection in MongoDB 3. Restart the game server +### Data saving issues between changing servers + +**Cause**: Not using our Velocity fork +**Solution**: + +1. Download our Velocity fork from [here](https://github.com/Swofty-Developments/Velocity/) +2. Replace your Velocity proxy with that version +3. Restart the proxy (which will also restart all game servers) + ### Players Can't See Each Other **Cause**: Multiple servers not syncing through Redis **Solution**: + 1. Verify Redis is running 2. Check all servers use the same `redis-uri` 3. Restart all game servers @@ -61,6 +76,7 @@ Common issues and their solutions. **Cause**: Auction service not running **Solution**: + 1. Start `ServiceAuctionHouse.jar` 2. Verify MongoDB connection 3. Check for errors in service logs @@ -70,6 +86,7 @@ Common issues and their solutions. **Cause**: Party service not running **Solution**: + 1. Start `ServiceParty.jar` 2. Verify Redis connection 3. Check service logs for errors @@ -79,6 +96,7 @@ Common issues and their solutions. ### World Not Loading **Checklist**: + 1. World folder exists in correct location 2. World folder name matches expected name exactly 3. World contains valid region files @@ -88,6 +106,7 @@ Common issues and their solutions. **Cause**: Data not imported **Solution**: Import these CSV files to MongoDB: + - `Minestom.regions.csv` → `regions` - `Minestom.fairysouls.csv` → `fairysouls` - `Minestom.crystals.csv` → `crystals` @@ -97,6 +116,7 @@ Common issues and their solutions. ### Containers Won't Start **Solutions**: + 1. Ensure Docker Desktop is running 2. Check for port conflicts (25565, 27017, 6379, 8080) 3. Review logs: `docker-compose logs` @@ -105,6 +125,7 @@ Common issues and their solutions. ### Can't Connect to Docker Server **Checklist**: + 1. Proxy container is healthy 2. At least one game server container is running 3. Connect to `localhost:25565` @@ -112,11 +133,13 @@ Common issues and their solutions. ### Containers Keep Restarting **Check logs**: + ```bash docker-compose logs -f ``` Common causes: + - Missing configuration files - Invalid `config.yml` - Database connection failures @@ -126,6 +149,7 @@ Common causes: ### High Memory Usage **Solutions**: + 1. Allocate appropriate memory per server: ```bash java -Xms2G -Xmx2G -jar HypixelCore.jar SKYBLOCK_HUB @@ -136,6 +160,7 @@ Common causes: ### Lag Spikes **Common causes**: + 1. Insufficient RAM 2. MongoDB on slow storage (use SSD) 3. Too many players per server instance @@ -148,9 +173,9 @@ If you're still having issues: 2. **Search existing issues** on [GitHub](https://github.com/Swofty-Developments/HypixelSkyBlock/issues) 3. **Join Discord** at [discord.gg/ZaGW5wzUJ3](https://discord.gg/ZaGW5wzUJ3) 4. **Ask in #code-help** with: - - Screenshots of all console outputs - - Your `config.yml` (remove secrets) - - Steps you've already tried + - Screenshots of all console outputs + - Your `config.yml` (remove secrets) + - Steps you've already tried :::alert warning Pinging staff members won't solve your issue faster!