Example service module

A complete process resource for a Minecraft server. It is short because the platform handles everything that is not game-specific.

Read Service modules first for what each method is responsible for.

<?php

    namespace GameDash\Sdk\Module\Implementation\Service\Minecraft\Resources\Process;

    use \Electrum\Userland\Sdk\Module\Gateway;
    use \GameDash\Sdk\Module\Template;
    use \GameDash\Sdk\FFI\Instance;
    use \GameDash\Sdk\FFI\Infrastructure\Node\Process\ChildProcess\ChildProcessNotFoundException;
    use \GameDash\Sdk\FFI\Infrastructure\Node\Process\ChildProcess\ChildProcessNotRunningException;

    class Process extends Template\Service\Process\Process {

        /** @var Instance\Instance */
        private $Instance;

        public function __construct( Gateway\Gateway $Gateway ) {

            // The instance id is passed in by the caller through the gateway.
            $instanceId = $Gateway->getParameters()->get('instance.id')->getValue();

            // Resolve once and hold it, so the other methods do not repeat the lookup.
            $this->Instance = Instance\Instances::get( $instanceId );

        }

        public function start(): void {

            // createDefault() applies platform defaults, including the relay channel that
            // carries console output back to the browser.
            $ChildProcess = $this->Instance->getProcess()->getChildProcesses()->createDefault();

            $ChildProcess->setExecutable('java');

            $ChildProcess->setArgs([

                '-jar',
                // Which jar to run is an instance setting, created during setup, so the
                // operator can change it without touching the module.
                $this->Instance->getSettings()->get('jar')->getValue(),
                'nogui'

            ]);

            $ChildProcess->spawn();

        }

        public function stop(): void {

            $Console = $this->Instance->getConsole();

            // Warn players and flush the world before the process goes away.
            $Console->getIo()->getInput()->send('say Server will shut down in 5 seconds');
            $Console->getIo()->getInput()->send('save-all');

            try {

                $ChildProcess = $this->Instance->getProcess()->getChildProcesses()->getCurrent();

                $ChildProcess->stop();

            }
            catch( ChildProcessNotRunningException $e ) {

                // Already stopped. Nothing to do.

            }

        }

        public function restart(): void {}

        public function isOnline(): bool {

            $Process = $this->Instance->getProcess();

            // An instance that has never started has no process id to look up.
            if( !$Process->hasId() ) {

                return false;

            }

            try {

                return !$Process->getChildProcesses()->getCurrent()->hasExited();

            }
            catch( ChildProcessNotFoundException $e ) {

                return false;

            }

        }

        // Lets the panel chart CPU and memory for this instance.
        public function usageIsMeasurable(): bool {

            return true;

        }

    }

?>

What to take from it

  • The constructor resolves the instance and nothing else. Every other method works from $this->Instance.
  • Nothing here is platform-specific. There is no branch on Linux versus Windows; the FFI handles that.
  • Both exceptions are treated as state, not failure. A process that is already gone is the normal case when stopping, and the absence of a child process is how you learn a server is offline.
  • Settings, not constants. The jar comes from instance settings, so one module serves every Minecraft instance regardless of version.

Next

The foreign function interface covers the classes used here, and the generated FFI class reference lists every method available.