Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ dependencies {
exclude group: 'io.netty'
exclude group: 'io.netty.incubator'
}
compileOnly libs.yamlConfigurate

implementation libs.sqlite
implementation libs.mysql
Expand Down
4 changes: 3 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
[versions]
geyser = "2.7.0-SNAPSHOT"
geyser = "2.9.1-SNAPSHOT"
sqlite = "3.49.1.0"
mysql = "9.2.0"
yamlConfigurate = "4.2.0-GeyserMC-20251111.004649-11"

[libraries]
geyser-core = { group = "org.geysermc.geyser", name = "core", version.ref = "geyser" }
geyser-api = { group = "org.geysermc.geyser", name = "api", version.ref = "geyser" }
sqlite = { group = "org.xerial", name = "sqlite-jdbc", version.ref = "sqlite" }
mysql = { group = "com.mysql", name = "mysql-connector-j", version.ref = "mysql" }
yamlConfigurate = { module = "org.spongepowered:configurate-yaml", version.ref = "yamlConfigurate" }

[bundles]
geyser = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,16 @@ public void onPostInitialize(GeyserPostInitializeEvent event) {

// Remove all saved logins to prevent issues connecting
// Maybe worth adding support for this later
geyserInstance.getConfig().getSavedUserLogins().clear();
geyserInstance.config().savedUserLogins().clear();

if (geyserInstance.getConfig().isPassthroughMotd() || geyserInstance.getConfig().isPassthroughPlayerCounts()) {
if (geyserInstance.config().motd().passthroughMotd() || geyserInstance.config().motd().passthroughPlayerCounts()) {
this.logger().warning("Either `passthrough-motd` or `passthrough-player-counts` is enabled in the config, this will likely produce errors");
}

// If we are using floodgate then disable the extension.
// GeyserConnect also doesn't support the connection sequence that occurs when the default RemoteServer
// auth-type is offline (and there is no reason to change it when GeyserConnect is in use).
if (geyserInstance.getConfig().getRemote().authType() != AuthType.ONLINE) {
if (geyserInstance.config().java().authType() != AuthType.ONLINE) {
this.logger().error("auth-type is not set to 'online' in the Geyser config, this will break GeyserConnect. Disabling!");
this.disable();
}
Expand All @@ -122,7 +122,7 @@ public void onPostInitialize(GeyserPostInitializeEvent event) {
public void onSessionInitialize(SessionInitializeEvent event) {
GeyserSession session = (GeyserSession) event.connection();
if (config().hardPlayerLimit()) {
if (session.getGeyser().getSessionManager().size() >= session.getGeyser().getConfig().getMaxPlayers()) {
if (session.getGeyser().getSessionManager().size() >= session.getGeyser().config().motd().maxPlayers()) {
session.disconnect("disconnectionScreen.serverFull");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public PacketHandler(GeyserConnect geyserConnect, GeyserSession session, Bedrock
}

@Override
public void onDisconnect(String reason) {
public void onDisconnect(CharSequence reason) {
// The user has disconnected without having connected to an actual server. If they have connected to
// a server (transfer packet or geyser proxy), then the original packet handler has been restored.
ServerManager.unloadServers(session);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@

package org.geysermc.extension.connect.config;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.geysermc.extension.connect.utils.Server;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;

import java.util.List;

@ConfigSerializable
public record Config(
@JsonProperty("welcome-file") String welcomeFile,
@JsonProperty("hard-player-limit") boolean hardPlayerLimit,
String welcomeFile,
boolean hardPlayerLimit,
List<Server> servers,
@JsonProperty("custom-servers") CustomServersSection customServers,
CustomServersSection customServers,
VirtualHostSection vhost) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,27 @@

package org.geysermc.extension.connect.config;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.geysermc.extension.connect.config.serializers.ServerCategorySerializer;
import org.geysermc.extension.connect.config.serializers.StorageTypeSerializer;
import org.geysermc.extension.connect.utils.ServerCategory;
import org.geysermc.geyser.api.extension.Extension;
import org.spongepowered.configurate.CommentedConfigurationNode;
import org.spongepowered.configurate.serialize.ScalarSerializer;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.yaml.NodeStyle;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.Collections;
import java.util.function.Predicate;

public class ConfigLoader {
public static <T> T load(Extension extension, Class<?> extensionClass, Class<T> configClass) {
Expand Down Expand Up @@ -74,9 +81,21 @@ public static <T> T load(Extension extension, Class<?> extensionClass, Class<T>

// Load the config file
try {
return new ObjectMapper(new YAMLFactory())
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.readValue(configFile, configClass);
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
.file(configFile)
.indent(2)
.nodeStyle(NodeStyle.BLOCK)
.defaultOptions(options ->
options.serializers(builder ->
builder.register(new StorageTypeSerializer())
.register(new ServerCategorySerializer())
)
)
.build();

CommentedConfigurationNode rootNode = loader.load();

return rootNode.get(configClass);
} catch (IOException e) {
extension.logger().error("Failed to load config", e);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@

package org.geysermc.extension.connect.config;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.geysermc.extension.connect.storage.AbstractStorageManager;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;

@ConfigSerializable
public record CustomServersSection(
boolean enabled,
int max,
@JsonProperty("storage-type") AbstractStorageManager.StorageType storageType,
AbstractStorageManager.StorageType storageType,
MySQLConnectionSection mysql) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@

package org.geysermc.extension.connect.config;

import org.spongepowered.configurate.objectmapping.ConfigSerializable;

@ConfigSerializable
public record MySQLConnectionSection(
String user,
String pass,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@

package org.geysermc.extension.connect.config;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;

import java.util.List;

@ConfigSerializable
public record VirtualHostSection(
boolean enabled,
@JsonProperty("domains") List<String> domains) {
List<String> domains) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2019-2025 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/GeyserConnect
*/

package org.geysermc.extension.connect.config.serializers;

import io.leangen.geantyref.TypeToken;
import org.geysermc.extension.connect.utils.ServerCategory;
import org.spongepowered.configurate.serialize.ScalarSerializer;
import org.spongepowered.configurate.serialize.Scalars;
import org.spongepowered.configurate.serialize.SerializationException;

import java.lang.reflect.Type;
import java.util.function.Predicate;

public class ServerCategorySerializer extends ScalarSerializer<ServerCategory> {
public ServerCategorySerializer() {
super(new TypeToken<>() {});
}

@Override
public ServerCategory deserialize(Type type, Object obj) throws SerializationException {
return (ServerCategory) Scalars.ENUM.deserialize(type, obj);
}

@Override
protected Object serialize(ServerCategory item, Predicate<Class<?>> typeSupported) {
return item.name();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2019-2025 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/GeyserConnect
*/

package org.geysermc.extension.connect.config.serializers;

import io.leangen.geantyref.TypeToken;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.extension.connect.storage.AbstractStorageManager;
import org.spongepowered.configurate.serialize.ScalarSerializer;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.util.EnumLookup;

import java.lang.reflect.Type;
import java.util.Locale;
import java.util.function.Predicate;

public final class StorageTypeSerializer extends ScalarSerializer<AbstractStorageManager.StorageType> {
public StorageTypeSerializer() {
super(new TypeToken<>() {});
}

@Override
public AbstractStorageManager.StorageType deserialize(Type type, Object obj) throws SerializationException {
final String enumConstant = obj.toString();
final AbstractStorageManager.@Nullable StorageType ret = EnumLookup.lookupEnum(AbstractStorageManager.StorageType.class, enumConstant);
if (ret == null) {
throw new SerializationException(type, "Invalid enum constant provided, expected a value of enum, got " + enumConstant);
}
return ret;
}

@Override
protected Object serialize(AbstractStorageManager.StorageType item, Predicate<Class<?>> typeSupported) {
return item.name().toLowerCase(Locale.ROOT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,12 @@

package org.geysermc.extension.connect.storage;

import com.fasterxml.jackson.core.type.TypeReference;
import com.google.gson.reflect.TypeToken;
import org.geysermc.extension.connect.GeyserConnect;
import org.geysermc.extension.connect.utils.Server;
import org.geysermc.extension.connect.utils.ServerManager;
import org.geysermc.extension.connect.utils.Utils;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
Expand Down Expand Up @@ -72,9 +71,9 @@ public void saveServers(org.geysermc.api.connection.Connection session) {
// replace into works on MySQL and SQLite
try (PreparedStatement updatePlayersServers = connection.prepareStatement("REPLACE INTO players(xuid, servers) VALUES(?, ?)")) {
updatePlayersServers.setString(1, session.xuid());
updatePlayersServers.setString(2, Utils.OBJECT_MAPPER.writeValueAsString(ServerManager.getServers(session)));
updatePlayersServers.setString(2, Utils.GSON.toJson(ServerManager.getServers(session)));
updatePlayersServers.executeUpdate();
} catch (IOException | SQLException exception) {
} catch (SQLException exception) {
GeyserConnect.instance().logger().error("Couldn't save servers for " + session.bedrockUsername(), exception);
}
}
Expand All @@ -88,13 +87,13 @@ public List<Server> loadServers(org.geysermc.api.connection.Connection session)
ResultSet rs = getPlayersServers.executeQuery();

while (rs.next()) {
List<Server> loadedServers = Utils.OBJECT_MAPPER.readValue(rs.getString("servers"), new TypeReference<>() {
List<Server> loadedServers = Utils.GSON.fromJson(rs.getString("servers"), new TypeToken<>() {
});
if (loadedServers != null) {
servers.addAll(loadedServers);
}
}
} catch (IOException | SQLException exception) {
} catch (SQLException exception) {
GeyserConnect.instance().logger().error("Couldn't load servers for " + session.bedrockUsername(), exception);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@

package org.geysermc.extension.connect.storage;

import com.fasterxml.jackson.annotation.JsonValue;
import com.google.gson.annotations.SerializedName;
import org.geysermc.api.connection.Connection;
import org.geysermc.extension.connect.utils.Server;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -47,25 +48,18 @@ public List<Server> loadServers(Connection session) {
return new ArrayList<>();
}

@ConfigSerializable
public enum StorageType {
JSON("json", JsonStorageManager.class),
SQLITE("sqlite", SQLiteStorageManager.class),
MYSQL("mysql", MySQLStorageManager.class);

@JsonValue
private final String configName;
JSON(JsonStorageManager.class),
SQLITE(SQLiteStorageManager.class),
MYSQL(MySQLStorageManager.class);

private final Class<? extends AbstractStorageManager> storageManager;

StorageType(String configName, Class<? extends AbstractStorageManager> storageManager) {
this.configName = configName;
StorageType(Class<? extends AbstractStorageManager> storageManager) {
this.storageManager = storageManager;
}

public String configName() {
return configName;
}

public Class<? extends AbstractStorageManager> storageManager() {
return storageManager;
}
Expand Down
Loading