@@ -3,6 +3,7 @@ package app.simplecloud.droplet.serverhost.runtime.runner
33import app.simplecloud.controller.shared.host.ServerHost
44import app.simplecloud.controller.shared.server.Server
55import app.simplecloud.droplet.serverhost.runtime.ServerHostRuntime
6+ import app.simplecloud.droplet.serverhost.runtime.config.environment.EnvironmentConfig
67import app.simplecloud.droplet.serverhost.runtime.config.environment.EnvironmentConfigRepository
78import app.simplecloud.droplet.serverhost.runtime.host.ServerVersionLoader
89import app.simplecloud.droplet.serverhost.runtime.launcher.ServerHostStartCommand
@@ -15,8 +16,13 @@ import app.simplecloud.droplet.serverhost.shared.actions.YamlActionPlaceholderCo
1516import app.simplecloud.droplet.serverhost.shared.actions.YamlActionTriggerTypes
1617import app.simplecloud.droplet.serverhost.shared.hack.PortProcessHandle
1718import app.simplecloud.droplet.serverhost.shared.hack.ServerPinger
19+ import app.simplecloud.droplet.serverhost.shared.logs.DefaultLogStreamer
20+ import app.simplecloud.droplet.serverhost.shared.logs.ScreenCommandExecutor
21+ import app.simplecloud.droplet.serverhost.shared.logs.ScreenConfigurer
1822import build.buf.gen.simplecloud.controller.v1.*
1923import kotlinx.coroutines.*
24+ import kotlinx.coroutines.flow.Flow
25+ import kotlinx.coroutines.flow.onCompletion
2026import kotlinx.coroutines.future.await
2127import kotlinx.coroutines.sync.Mutex
2228import kotlinx.coroutines.sync.withLock
@@ -30,17 +36,17 @@ import java.nio.file.Paths
3036import kotlin.io.path.absolutePathString
3137import kotlin.io.path.exists
3238
33- class ServerRunner (
39+ class DefaultServerEnvironment (
3440 private val templateProvider : TemplateProvider ,
3541 private val serverHost : ServerHost ,
3642 private val args : ServerHostStartCommand ,
3743 private val controllerStub : ControllerServerServiceGrpcKt .ControllerServerServiceCoroutineStub ,
3844 private val metricsTracker : MetricsTracker ,
3945 private val environmentsRepository : EnvironmentConfigRepository ,
40- ) {
46+ override val runtimeRepository : GroupRuntimeDirectory ,
47+ ) : ServerEnvironment(runtimeRepository, environmentsRepository) {
4148
4249 private val copyTemplateMutex = Mutex ()
43- private val runtimeRepository = GroupRuntimeDirectory ()
4450
4551 private val stopTries = mutableMapOf<String , Int >()
4652 private val maxGracefulTries = 3
@@ -57,7 +63,7 @@ class ServerRunner(
5763 return serverToProcessHandle.any { it.key.uniqueId == uniqueId }
5864 }
5965
60- fun getServer (uniqueId : String ): Server ? {
66+ override fun getServer (uniqueId : String ): Server ? {
6167 return serverToProcessHandle.keys.find { it.uniqueId == uniqueId }
6268 }
6369
@@ -79,7 +85,7 @@ class ServerRunner(
7985
8086 // Retrieving this before the ping makes it possible to stop servers way sooner (port is registered in system nearly instantly, it takes longer for the
8187 // server to respond to pings though)
82- val executable = environmentsRepository.get(runtimeRepository.get( server.group) )
88+ val executable = getEnvironment( server)
8389 val handle = PortProcessHandle .of(server.port.toInt()).orElse(null )?.let {
8490 val realProcess = executable?.getRealExecutable()?.let { exe ->
8591 ProcessFinder .findHighestProcessWorkingDir(
@@ -164,14 +170,14 @@ class ServerRunner(
164170 return File (basicUrl)
165171 }
166172
167- fun getServerLogFile (server : Server ): Path {
173+ private fun getServerLogFile (server : Server ): Path {
168174 return Paths .get(
169175 args.logsPath.absolutePathString(),
170176 " ${server.group} -${server.numericalId} -${server.uniqueId} .log"
171177 )
172178 }
173179
174- suspend fun startServer (server : Server ): Boolean {
180+ override suspend fun startServer (server : Server ): Boolean {
175181 logger.info(" Starting server ${server.uniqueId} of group ${server.group} (#${server.numericalId} )" )
176182
177183 if (containsServer(server)) {
@@ -235,7 +241,7 @@ class ServerRunner(
235241 else
236242 logger.error(" Template ${server.properties[" template-id" ] ? : " " } of group ${server.group} was not found!" )
237243 }
238- return null ;
244+ return null
239245 }
240246
241247 private fun getServerDir (ctx : YamlActionContext ): File ? {
@@ -244,7 +250,7 @@ class ServerRunner(
244250 return Paths .get(placeholders.parse(dir)).toFile()
245251 }
246252
247- suspend fun stopServer (server : Server ): Boolean {
253+ override suspend fun stopServer (server : Server ): Boolean {
248254 logger.info(" Stopping server ${server.uniqueId} of group ${server.group} (#${server.numericalId} )" )
249255 val stopped = stopServer(server.uniqueId, stopTries.getOrDefault(server.uniqueId, 0 ) >= maxGracefulTries)
250256 if (! stopped) return false
@@ -271,9 +277,7 @@ class ServerRunner(
271277 return false
272278 }
273279
274- val load = runtimeRepository.get(server.group)
275-
276- val env = environmentsRepository.get(load)
280+ val env = getEnvironment(server)
277281 if (env != null && env.useScreenStop) {
278282 terminateScreenSession(process.pid())
279283 } else {
@@ -295,19 +299,20 @@ class ServerRunner(
295299 }
296300 }
297301
298- fun getProcess (uniqueId : String ): ProcessHandle ? {
302+ private fun getProcess (uniqueId : String ): ProcessHandle ? {
299303 return serverToProcessHandle.getOrDefault(
300304 serverToProcessHandle.keys.firstOrNull { it.uniqueId == uniqueId },
301305 null
302306 )
303307 }
304308
305- fun reattachServer (server : Server ): Boolean {
309+
310+ override fun reattachServer (server : Server ): Boolean {
306311 if (containsServer(server.uniqueId)) {
307312 logger.error(" Server ${server.uniqueId} of group ${server.group} is already running." )
308313 return true
309314 }
310- val executable = environmentsRepository.get(runtimeRepository.get( server.group) )?.getRealExecutable()
315+ val executable = getEnvironment( server)?.getRealExecutable()
311316 val handle = PortProcessHandle .of(server.port.toInt()).orElse(null )
312317 ?.let {
313318 executable?.let { exe ->
@@ -322,7 +327,6 @@ class ServerRunner(
322327 if (handle == null ) {
323328 logger.error(" Server ${server.uniqueId} of group ${server.group} not found running on port ${server.port} . Is it down?" )
324329 executeTemplate(getServerDir(server).toPath(), server, YamlActionTriggerTypes .STOP )
325- FileUtils .deleteDirectory(getServerDir(server))
326330 PortProcessHandle .removePreBind(server.port.toInt(), true )
327331 return false
328332 }
@@ -332,6 +336,37 @@ class ServerRunner(
332336 return true
333337 }
334338
339+ override fun executeCommand (server : Server , command : String ): Boolean {
340+ if (getEnvironment(server)?.isScreen != true ) return false
341+ val process = getProcess(server.uniqueId)
342+ ? : return false
343+ val streamer = ScreenCommandExecutor (process.pid())
344+ streamer.sendCommand(command)
345+ return true
346+ }
347+
348+ override fun streamLogs (server : Server ): Flow <ServerHostStreamServerLogsResponse > {
349+ var configurer: ScreenConfigurer ? = null
350+ try {
351+ val process = getProcess(server.uniqueId)
352+ if (process != null ) {
353+ configurer = ScreenConfigurer (process.pid())
354+ configurer.setLogsFlush(0 )
355+ logger.warn(" Screen streaming for server ${server.group} -${server.numericalId} (${server.uniqueId} ) not available, log stream will be slower." )
356+ }
357+ val fileStreamer = DefaultLogStreamer (getServerLogFile(server))
358+ return fileStreamer.readScreenLogs().onCompletion { configurer?.setLogsFlush(10 ) }
359+ } catch (e: Exception ) {
360+ configurer?.setLogsFlush(10 )
361+ logger.error(" Failed to stream server logs" , e)
362+ throw e
363+ }
364+ }
365+
366+ override fun appliesFor (env : EnvironmentConfig ): Boolean {
367+ return env.enabled && env.start?.command != null
368+ }
369+
335370 private fun createRuntimePlaceholders (server : Server ): MutableMap <String , String > {
336371 val placeholders = mutableMapOf (
337372 " %MIN_MEMORY%" to server.minMemory.toString(),
@@ -419,7 +454,7 @@ class ServerRunner(
419454 }
420455
421456
422- fun startServerStateChecker (): Job {
457+ override fun startServerStateChecker (): Job {
423458 return CoroutineScope (Dispatchers .IO ).launch {
424459 while (isActive) {
425460 serverToProcessHandle.keys.toList().forEach {
0 commit comments