From 33d589a9c52d50763b870581affb2c119e3c1125 Mon Sep 17 00:00:00 2001 From: AlexProgrammerDE <40795980+AlexProgrammerDE@users.noreply.github.com> Date: Sun, 29 Dec 2024 15:16:23 +0100 Subject: [PATCH] Implement separate server settings --- proto/src/main/proto/soulfire/common.proto | 4 + .../soulfiremc/server/InstanceManager.java | 2 - .../com/soulfiremc/server/SoulFireServer.java | 28 ++++-- .../server/database/DatabaseManager.java | 3 +- .../server/database/InstanceEntity.java | 2 +- ...er.java => InstanceSettingsConverter.java} | 2 +- .../server/database/ServerConfigEntity.java | 39 ++++++++ .../database/ServerSettingsConverter.java | 37 ++++++++ .../com/soulfiremc/server/grpc/RPCServer.java | 1 + .../server/grpc/ServerServiceImpl.java | 88 +++++++++++++++++++ .../settings/lib/ServerSettingsImpl.java | 8 +- 11 files changed, 200 insertions(+), 14 deletions(-) rename server/src/main/java/com/soulfiremc/server/database/{SettingsConverter.java => InstanceSettingsConverter.java} (93%) create mode 100644 server/src/main/java/com/soulfiremc/server/database/ServerConfigEntity.java create mode 100644 server/src/main/java/com/soulfiremc/server/database/ServerSettingsConverter.java create mode 100644 server/src/main/java/com/soulfiremc/server/grpc/ServerServiceImpl.java diff --git a/proto/src/main/proto/soulfire/common.proto b/proto/src/main/proto/soulfire/common.proto index 81286bcf..c30effdb 100644 --- a/proto/src/main/proto/soulfire/common.proto +++ b/proto/src/main/proto/soulfire/common.proto @@ -95,6 +95,10 @@ enum GlobalPermission { READ_CLIENT_DATA = 2; READ_SERVER_CONFIG = 3; UPDATE_SERVER_CONFIG = 4; + CREATE_USER = 5; + READ_USER = 6; + UPDATE_USER = 7; + DELETE_USER = 8; } enum InstancePermission { diff --git a/server/src/main/java/com/soulfiremc/server/InstanceManager.java b/server/src/main/java/com/soulfiremc/server/InstanceManager.java index 46b27acf..ea5db4d2 100644 --- a/server/src/main/java/com/soulfiremc/server/InstanceManager.java +++ b/server/src/main/java/com/soulfiremc/server/InstanceManager.java @@ -193,8 +193,6 @@ private void start() { throw new IllegalStateException("Another attack is still running"); } - SoulFireServer.setupLoggingAndVia(settingsSource); - this.attackLifecycle(AttackLifecycle.STARTING); var address = settingsSource.get(BotSettings.ADDRESS); diff --git a/server/src/main/java/com/soulfiremc/server/SoulFireServer.java b/server/src/main/java/com/soulfiremc/server/SoulFireServer.java index acac945c..5b834273 100644 --- a/server/src/main/java/com/soulfiremc/server/SoulFireServer.java +++ b/server/src/main/java/com/soulfiremc/server/SoulFireServer.java @@ -29,10 +29,11 @@ import com.soulfiremc.server.data.TranslationMapper; import com.soulfiremc.server.database.DatabaseManager; import com.soulfiremc.server.database.InstanceEntity; +import com.soulfiremc.server.database.ServerConfigEntity; import com.soulfiremc.server.database.UserEntity; import com.soulfiremc.server.grpc.RPCServer; import com.soulfiremc.server.settings.*; -import com.soulfiremc.server.settings.lib.InstanceSettingsSource; +import com.soulfiremc.server.settings.lib.ServerSettingsDelegate; import com.soulfiremc.server.settings.lib.ServerSettingsRegistry; import com.soulfiremc.server.spark.SFSparkPlugin; import com.soulfiremc.server.user.AuthSystem; @@ -41,6 +42,7 @@ import com.soulfiremc.server.util.SFPathConstants; import com.soulfiremc.server.util.SFUpdateChecker; import com.soulfiremc.server.util.TimeUtil; +import com.soulfiremc.server.util.structs.CachedLazyObject; import com.soulfiremc.server.util.structs.ShutdownManager; import com.soulfiremc.server.viaversion.SFVLLoaderImpl; import com.soulfiremc.server.viaversion.SFViaPlatform; @@ -67,6 +69,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; /** * The main class of the SoulFire server. @@ -90,6 +93,7 @@ public class SoulFireServer { private final SoulFireScheduler scheduler = new SoulFireScheduler(log); private final Map instances = new ConcurrentHashMap<>(); private final MetadataHolder metadata = new MetadataHolder(); + private final ServerSettingsDelegate settingsSource; private final RPCServer rpcServer; private final AuthSystem authSystem; private final ServerSettingsRegistry serverSettingsRegistry; @@ -117,6 +121,16 @@ public SoulFireServer( this.sessionFactory = DatabaseManager.forSqlite(baseDirectory.resolve("soulfire.sqlite")); injector.register(SessionFactory.class, sessionFactory); + this.settingsSource = new ServerSettingsDelegate(new CachedLazyObject<>(() -> + sessionFactory.fromTransaction(session -> { + var entity = session.find(ServerConfigEntity.class, 1); + if (entity == null) { + entity = new ServerConfigEntity(); + session.persist(entity); + } + + return entity.settings(); + }), 1, TimeUnit.SECONDS)); this.authSystem = new AuthSystem(this); this.rpcServer = new RPCServer(host, port, injector, authSystem); @@ -156,6 +170,9 @@ public SoulFireServer( CompletableFuture.allOf(viaStart, sparkStart, updateCheck).join(); + // Via is ready, we can now set up all config stuff + setupLoggingAndVia(); + var newVersion = updateCheck.join(); if (newVersion != null) { log.warn( @@ -202,12 +219,13 @@ public SoulFireServer( "Finished loading! (Took {}ms)", Duration.between(startTime, Instant.now()).toMillis()); } - public static void setupLoggingAndVia(InstanceSettingsSource settingsSource) { - Via.getManager().debugHandler().setEnabled(settingsSource.get(DevSettings.VIA_DEBUG)); - setupLogging(settingsSource); + public void configUpdateHook() { + setupLoggingAndVia(); } - public static void setupLogging(InstanceSettingsSource settingsSource) { + public void setupLoggingAndVia() { + Via.getManager().debugHandler().setEnabled(settingsSource.get(DevSettings.VIA_DEBUG)); + Configurator.setRootLevel(settingsSource.get(DevSettings.CORE_DEBUG) ? Level.DEBUG : Level.INFO); Configurator.setLevel("io.netty", settingsSource.get(DevSettings.NETTY_DEBUG) ? Level.DEBUG : Level.INFO); Configurator.setLevel("io.grpc", settingsSource.get(DevSettings.GRPC_DEBUG) ? Level.DEBUG : Level.INFO); diff --git a/server/src/main/java/com/soulfiremc/server/database/DatabaseManager.java b/server/src/main/java/com/soulfiremc/server/database/DatabaseManager.java index 01d164b1..12b766f0 100644 --- a/server/src/main/java/com/soulfiremc/server/database/DatabaseManager.java +++ b/server/src/main/java/com/soulfiremc/server/database/DatabaseManager.java @@ -46,7 +46,8 @@ public static SessionFactory forSqlite(Path dbFile) { metadataSources.addPackage("com.soulfiremc.server.database"); metadataSources.addAnnotatedClasses( UserEntity.class, - InstanceEntity.class + InstanceEntity.class, + ServerConfigEntity.class ); return metadataSources.getMetadataBuilder() diff --git a/server/src/main/java/com/soulfiremc/server/database/InstanceEntity.java b/server/src/main/java/com/soulfiremc/server/database/InstanceEntity.java index b83d040b..33a7d541 100644 --- a/server/src/main/java/com/soulfiremc/server/database/InstanceEntity.java +++ b/server/src/main/java/com/soulfiremc/server/database/InstanceEntity.java @@ -48,7 +48,7 @@ public class InstanceEntity { @Column(nullable = false) private AttackLifecycle attackLifecycle = AttackLifecycle.STOPPED; - @Convert(converter = SettingsConverter.class) + @Convert(converter = InstanceSettingsConverter.class) @Column(nullable = false) private InstanceSettingsImpl settings = InstanceSettingsImpl.EMPTY; diff --git a/server/src/main/java/com/soulfiremc/server/database/SettingsConverter.java b/server/src/main/java/com/soulfiremc/server/database/InstanceSettingsConverter.java similarity index 93% rename from server/src/main/java/com/soulfiremc/server/database/SettingsConverter.java rename to server/src/main/java/com/soulfiremc/server/database/InstanceSettingsConverter.java index 17ac7733..46489e9a 100644 --- a/server/src/main/java/com/soulfiremc/server/database/SettingsConverter.java +++ b/server/src/main/java/com/soulfiremc/server/database/InstanceSettingsConverter.java @@ -24,7 +24,7 @@ import jakarta.persistence.Converter; @Converter(autoApply = true) -public class SettingsConverter implements AttributeConverter { +public class InstanceSettingsConverter implements AttributeConverter { @Override public String convertToDatabaseColumn(InstanceSettingsImpl attribute) { return GsonInstance.GSON.toJson(attribute.serializeToTree()); diff --git a/server/src/main/java/com/soulfiremc/server/database/ServerConfigEntity.java b/server/src/main/java/com/soulfiremc/server/database/ServerConfigEntity.java new file mode 100644 index 00000000..792436e6 --- /dev/null +++ b/server/src/main/java/com/soulfiremc/server/database/ServerConfigEntity.java @@ -0,0 +1,39 @@ +/* + * SoulFire + * Copyright (C) 2024 AlexProgrammerDE + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.soulfiremc.server.database; + +import com.soulfiremc.server.settings.lib.ServerSettingsImpl; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "server_config") +public class ServerConfigEntity { + @Id + private Long id = 1L; + + @Convert(converter = ServerSettingsConverter.class) + @Column(nullable = false) + private ServerSettingsImpl settings = ServerSettingsImpl.EMPTY; + + @Version + private long version; +} diff --git a/server/src/main/java/com/soulfiremc/server/database/ServerSettingsConverter.java b/server/src/main/java/com/soulfiremc/server/database/ServerSettingsConverter.java new file mode 100644 index 00000000..d96fcaa6 --- /dev/null +++ b/server/src/main/java/com/soulfiremc/server/database/ServerSettingsConverter.java @@ -0,0 +1,37 @@ +/* + * SoulFire + * Copyright (C) 2024 AlexProgrammerDE + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.soulfiremc.server.database; + +import com.google.gson.JsonElement; +import com.soulfiremc.server.settings.lib.ServerSettingsImpl; +import com.soulfiremc.server.util.structs.GsonInstance; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +@Converter(autoApply = true) +public class ServerSettingsConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(ServerSettingsImpl attribute) { + return GsonInstance.GSON.toJson(attribute.serializeToTree()); + } + + @Override + public ServerSettingsImpl convertToEntityAttribute(String dbData) { + return ServerSettingsImpl.deserialize(GsonInstance.GSON.fromJson(dbData, JsonElement.class)); + } +} diff --git a/server/src/main/java/com/soulfiremc/server/grpc/RPCServer.java b/server/src/main/java/com/soulfiremc/server/grpc/RPCServer.java index eb10afc3..0834c0fd 100644 --- a/server/src/main/java/com/soulfiremc/server/grpc/RPCServer.java +++ b/server/src/main/java/com/soulfiremc/server/grpc/RPCServer.java @@ -98,6 +98,7 @@ public RPCServer( .addService(injector.getSingleton(ProxyCheckServiceImpl.class)) .addService(injector.getSingleton(DownloadServiceImpl.class)) .addService(injector.getSingleton(ObjectStorageServiceImpl.class)) + .addService(injector.getSingleton(ServerServiceImpl.class)) // Allow collecting info about callable methods. .addService(ProtoReflectionServiceV1.newInstance()) .maxRequestMessageLength(Integer.MAX_VALUE) diff --git a/server/src/main/java/com/soulfiremc/server/grpc/ServerServiceImpl.java b/server/src/main/java/com/soulfiremc/server/grpc/ServerServiceImpl.java new file mode 100644 index 00000000..74df24b8 --- /dev/null +++ b/server/src/main/java/com/soulfiremc/server/grpc/ServerServiceImpl.java @@ -0,0 +1,88 @@ +/* + * SoulFire + * Copyright (C) 2024 AlexProgrammerDE + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.soulfiremc.server.grpc; + +import com.soulfiremc.grpc.generated.*; +import com.soulfiremc.server.SoulFireServer; +import com.soulfiremc.server.database.ServerConfigEntity; +import com.soulfiremc.server.settings.lib.ServerSettingsImpl; +import com.soulfiremc.server.user.PermissionContext; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.SessionFactory; + +import javax.inject.Inject; + +@Slf4j +@RequiredArgsConstructor(onConstructor_ = @Inject) +public class ServerServiceImpl extends ServerServiceGrpc.ServerServiceImplBase { + private final SoulFireServer soulFireServer; + private final SessionFactory sessionFactory; + + @Override + public void getServerInfo(ServerInfoRequest request, StreamObserver responseObserver) { + ServerRPCConstants.USER_CONTEXT_KEY.get().hasPermissionOrThrow(PermissionContext.global(GlobalPermission.READ_SERVER_CONFIG)); + + try { + var configEntity = sessionFactory.fromTransaction(session -> session.find(ServerConfigEntity.class, 1)); + ServerSettingsImpl config; + if (configEntity == null) { + config = ServerSettingsImpl.EMPTY; + } else { + config = configEntity.settings(); + } + + responseObserver.onNext(ServerInfoResponse.newBuilder() + .setConfig(config.toProto()) + .build()); + responseObserver.onCompleted(); + } catch (Throwable t) { + log.error("Error getting server info", t); + throw new StatusRuntimeException(Status.INTERNAL.withDescription(t.getMessage()).withCause(t)); + } + } + + @Override + public void updateServerConfig(ServerUpdateConfigRequest request, StreamObserver responseObserver) { + ServerRPCConstants.USER_CONTEXT_KEY.get().hasPermissionOrThrow(PermissionContext.global(GlobalPermission.UPDATE_SERVER_CONFIG)); + + try { + sessionFactory.inTransaction(session -> { + var currentConfigEntity = session.find(ServerConfigEntity.class, 1); + if (currentConfigEntity == null) { + var newConfigEntity = new ServerConfigEntity(); + newConfigEntity.settings(ServerSettingsImpl.fromProto(request.getConfig())); + session.persist(newConfigEntity); + } else { + currentConfigEntity.settings(ServerSettingsImpl.fromProto(request.getConfig())); + session.merge(currentConfigEntity); + } + }); + + soulFireServer.configUpdateHook(); + responseObserver.onNext(ServerUpdateConfigResponse.newBuilder().build()); + responseObserver.onCompleted(); + } catch (Throwable t) { + log.error("Error getting server info", t); + throw new StatusRuntimeException(Status.INTERNAL.withDescription(t.getMessage()).withCause(t)); + } + } +} diff --git a/server/src/main/java/com/soulfiremc/server/settings/lib/ServerSettingsImpl.java b/server/src/main/java/com/soulfiremc/server/settings/lib/ServerSettingsImpl.java index 30c24e68..4462b77c 100644 --- a/server/src/main/java/com/soulfiremc/server/settings/lib/ServerSettingsImpl.java +++ b/server/src/main/java/com/soulfiremc/server/settings/lib/ServerSettingsImpl.java @@ -23,7 +23,7 @@ import com.google.gson.JsonObject; import com.google.protobuf.Value; import com.google.protobuf.util.JsonFormat; -import com.soulfiremc.grpc.generated.InstanceConfig; +import com.soulfiremc.grpc.generated.ServerConfig; import com.soulfiremc.grpc.generated.SettingsEntry; import com.soulfiremc.grpc.generated.SettingsNamespace; import com.soulfiremc.server.settings.PropertyKey; @@ -51,7 +51,7 @@ public static ServerSettingsImpl deserialize(JsonElement json) { } @SneakyThrows - public static ServerSettingsImpl fromProto(InstanceConfig request) { + public static ServerSettingsImpl fromProto(ServerConfig request) { var settingsProperties = new HashMap>(); for (var namespace : request.getSettingsList()) { @@ -72,7 +72,7 @@ public JsonObject serializeToTree() { } @SneakyThrows - public InstanceConfig toProto() { + public ServerConfig toProto() { var settingsProperties = new HashMap>(); for (var entry : this.settings.entrySet()) { var namespace = entry.getKey(); @@ -91,7 +91,7 @@ public InstanceConfig toProto() { settingsProperties.put(namespace, innerMap); } - return InstanceConfig.newBuilder() + return ServerConfig.newBuilder() .addAllSettings(settingsProperties.entrySet().stream().map(entry -> { var namespace = entry.getKey();