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
399 changes: 383 additions & 16 deletions generated/schema.graphql

Large diffs are not rendered by default.

443 changes: 405 additions & 38 deletions generated/schema.ts

Large diffs are not rendered by default.

46,818 changes: 23,646 additions & 23,172 deletions generated/types.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions hasura/enums/plugin-runtimes.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
insert into e_plugin_runtimes ("value", "description") values
('swiftlys2', 'Plugin loads under the SwiftlyS2 framework'),
('counterstrikesharp', 'Plugin loads under Metamod and CounterStrikeSharp')
on conflict(value) do update set "description" = EXCLUDED."description"
6 changes: 5 additions & 1 deletion hasura/functions/game-server-node/plugin-supported.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ begin
return true;
end if;

return game_version_supports_plugin(game_server_node.build_id, game_server_node.pin_plugin_version);
return game_version_supports_plugin(
game_server_node.build_id,
game_server_node.pin_plugin_version,
coalesce(game_server_node.pin_plugin_runtime, active_plugin_runtime())
);
end;
$$ language plpgsql stable;
15 changes: 15 additions & 0 deletions hasura/functions/plugin-versions/active-plugin-runtime.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
create or replace function active_plugin_runtime() returns text as $$
declare
runtime text;
begin
select value from settings
where name = 'public.game_server_plugin_runtime'
into runtime;

if runtime is null or not exists (select 1 from e_plugin_runtimes where value = runtime) then
return 'swiftlys2';
end if;

return runtime;
end;
$$ language plpgsql stable;
21 changes: 14 additions & 7 deletions hasura/functions/plugin-versions/game-version-supports-plugin.sql
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
create or replace function game_version_supports_plugin(game_build_id integer, plugin_version text) returns boolean as $$
create or replace function game_version_supports_plugin(game_build_id integer, plugin_version text, plugin_runtime text default null) returns boolean as $$
declare
plugin_version_record plugin_versions%rowtype;
target_runtime text;
begin
target_runtime := coalesce(plugin_runtime, active_plugin_runtime());

if plugin_version is null then
select * from plugin_versions
select * from plugin_versions
where min_game_build_id is not null
order by published_at desc
limit 1
and plugin_versions.runtime = target_runtime
order by published_at desc
limit 1
into plugin_version_record;
else
select * from plugin_versions where version = plugin_version into plugin_version_record;
select * from plugin_versions
where version = plugin_version
and plugin_versions.runtime = target_runtime
into plugin_version_record;

if(plugin_version_record.min_game_build_id is null) then
select * from plugin_versions
select * from plugin_versions
where min_game_build_id is not null
and plugin_versions.runtime = target_runtime
AND published_at <= plugin_version_record.published_at
order by published_at desc
into plugin_version_record;
Expand All @@ -27,4 +35,3 @@ begin
return game_build_id >= plugin_version_record.min_game_build_id;
end;
$$ language plpgsql stable;

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
table:
name: e_plugin_runtimes
schema: public
is_enum: true
select_permissions:
- role: administrator
permission:
columns:
- description
- value
filter: {}
comment: ""
- role: user
permission:
columns:
- description
- value
filter: {}
comment: ""
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ select_permissions:
- node_ip
- offline_at
- pin_build_id
- pin_plugin_runtime
- pin_plugin_version
- public_ip
- region
Expand Down Expand Up @@ -112,6 +113,7 @@ update_permissions:
- gpu_streaming_enabled
- label
- pin_build_id
- pin_plugin_runtime
- pin_plugin_version
- region
- start_port_range
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ select_permissions:
columns:
- min_game_build_id
- published_at
- runtime
- version
filter: {}
comment: ""
4 changes: 4 additions & 0 deletions hasura/metadata/databases/default/tables/public_servers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ select_permissions:
- label
- max_players
- offline_at
- plugin_runtime
- plugin_version
- port
- rcon_status
Expand All @@ -107,6 +108,7 @@ select_permissions:
- id
- label
- max_players
- plugin_runtime
- plugin_version
- port
- rcon_status
Expand Down Expand Up @@ -137,6 +139,7 @@ select_permissions:
- is_dedicated
- label
- max_players
- plugin_runtime
- plugin_version
- port
- rcon_status
Expand Down Expand Up @@ -165,6 +168,7 @@ select_permissions:
- is_dedicated
- label
- max_players
- plugin_runtime
- plugin_version
- port
- rcon_status
Expand Down
1 change: 1 addition & 0 deletions hasura/metadata/databases/default/tables/tables.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
- "!include public_e_notification_types.yaml"
- "!include public_e_objective_types.yaml"
- "!include public_e_player_roles.yaml"
- "!include public_e_plugin_runtimes.yaml"
- "!include public_e_ready_settings.yaml"
- "!include public_e_sanction_types.yaml"
- "!include public_e_scrim_request_statuses.yaml"
Expand Down
48 changes: 48 additions & 0 deletions hasura/migrations/default/1870000000000_plugin_runtimes/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
DROP FUNCTION IF EXISTS public.game_version_supports_plugin(integer, text, text);
DROP FUNCTION IF EXISTS public.active_plugin_runtime();

ALTER TABLE public.servers
DROP CONSTRAINT IF EXISTS "servers_plugin_runtime_fkey";

ALTER TABLE public.servers
DROP COLUMN IF EXISTS "plugin_runtime";

ALTER TABLE public.game_server_nodes
DROP CONSTRAINT IF EXISTS "game_server_nodes_pin_plugin_version_fkey";

ALTER TABLE public.game_server_nodes
DROP CONSTRAINT IF EXISTS "game_server_nodes_pin_plugin_version_check";

-- these pins survive the DELETE below and then fail the single-column foreign key
-- when it is re-added
UPDATE public.game_server_nodes
SET "pin_plugin_version" = NULL
WHERE "pin_plugin_version" IS NOT NULL
AND "pin_plugin_runtime" IS DISTINCT FROM 'counterstrikesharp';

ALTER TABLE public.game_server_nodes
DROP COLUMN IF EXISTS "pin_plugin_runtime";

ALTER TABLE public.plugin_versions
DROP CONSTRAINT IF EXISTS "plugin_versions_runtime_fkey";

ALTER TABLE public.plugin_versions
DROP CONSTRAINT IF EXISTS "plugin_versions_pkey";

-- Collapsing back to a single lineage: keep only the CounterStrikeSharp rows,
-- which is what the pre-migration release feed produced.
DELETE FROM public.plugin_versions WHERE "runtime" <> 'counterstrikesharp';

ALTER TABLE public.plugin_versions
DROP COLUMN IF EXISTS "runtime";

ALTER TABLE public.plugin_versions
ADD CONSTRAINT "plugin_versions_pkey" PRIMARY KEY ("version");

ALTER TABLE public.game_server_nodes
ADD CONSTRAINT "game_server_nodes_pin_plugin_version_fkey"
FOREIGN KEY ("pin_plugin_version")
REFERENCES public.plugin_versions ("version")
ON UPDATE CASCADE ON DELETE SET NULL;

DROP TABLE IF EXISTS public.e_plugin_runtimes;
66 changes: 66 additions & 0 deletions hasura/migrations/default/1870000000000_plugin_runtimes/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
CREATE TABLE IF NOT EXISTS public.e_plugin_runtimes (
value text NOT NULL PRIMARY KEY,
description text NOT NULL
);

INSERT INTO e_plugin_runtimes ("value", "description") VALUES
('swiftlys2', 'Plugin loads under the SwiftlyS2 framework'),
('counterstrikesharp', 'Plugin loads under Metamod and CounterStrikeSharp')
ON CONFLICT(value) DO UPDATE SET "description" = EXCLUDED."description";

-- Every row already in plugin_versions was ingested from the CounterStrikeSharp
-- plugin repo, which was the only release feed before this migration.
ALTER TABLE public.plugin_versions
ADD COLUMN IF NOT EXISTS "runtime" text NOT NULL DEFAULT 'counterstrikesharp';

ALTER TABLE public.plugin_versions
ALTER COLUMN "runtime" DROP DEFAULT;

ALTER TABLE public.game_server_nodes
DROP CONSTRAINT IF EXISTS "game_server_nodes_pin_plugin_version_fkey";

-- Both plugin repos mint tags from their own counter, so v0.0.42 exists in each.
-- The version alone can no longer identify a release.
ALTER TABLE public.plugin_versions
DROP CONSTRAINT IF EXISTS "plugin_versions_pkey";

ALTER TABLE public.plugin_versions
ADD CONSTRAINT "plugin_versions_pkey" PRIMARY KEY ("runtime", "version");

ALTER TABLE public.plugin_versions
ADD CONSTRAINT "plugin_versions_runtime_fkey"
FOREIGN KEY ("runtime")
REFERENCES public.e_plugin_runtimes ("value")
ON UPDATE CASCADE ON DELETE RESTRICT;

ALTER TABLE public.game_server_nodes
ADD COLUMN IF NOT EXISTS "pin_plugin_runtime" text NULL;

UPDATE public.game_server_nodes
SET "pin_plugin_runtime" = 'counterstrikesharp'
WHERE "pin_plugin_version" IS NOT NULL
AND "pin_plugin_runtime" IS NULL;

ALTER TABLE public.game_server_nodes
ADD CONSTRAINT "game_server_nodes_pin_plugin_version_check"
CHECK (("pin_plugin_runtime" IS NULL) = ("pin_plugin_version" IS NULL));

ALTER TABLE public.game_server_nodes
ADD CONSTRAINT "game_server_nodes_pin_plugin_version_fkey"
FOREIGN KEY ("pin_plugin_runtime", "pin_plugin_version")
REFERENCES public.plugin_versions ("runtime", "version")
ON UPDATE CASCADE ON DELETE SET NULL;

-- game_version_supports_plugin gains a runtime argument. Adding a parameter makes
-- `create or replace` mint an overload rather than replace, so the old signature
-- has to go before hasura/functions is re-applied on boot.
DROP FUNCTION IF EXISTS public.game_version_supports_plugin(integer, text);

ALTER TABLE public.servers
ADD COLUMN IF NOT EXISTS "plugin_runtime" text NULL;

ALTER TABLE public.servers
ADD CONSTRAINT "servers_plugin_runtime_fkey"
FOREIGN KEY ("plugin_runtime")
REFERENCES public.e_plugin_runtimes ("value")
ON UPDATE CASCADE ON DELETE SET NULL;
9 changes: 7 additions & 2 deletions src/configs/game-servers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ export default (): {
gameServers: GameServersConfig;
} => ({
gameServers: {
serverImage:
process.env.SERVER_IMAGE || "ghcr.io/5stackgg/game-server:latest",
// The runtime is picked in application settings. SERVER_IMAGE remains an
// escape hatch for custom or mirrored images and takes precedence over it.
serverImageOverride: process.env.SERVER_IMAGE || null,
pluginRuntimeImages: {
swiftlys2: "ghcr.io/5stackgg/swiftly-game-server",
counterstrikesharp: "ghcr.io/5stackgg/game-server",
},
gameStreamerImage:
process.env.GAME_STREAMER_IMAGE ||
"ghcr.io/5stackgg/game-streamer:latest",
Expand Down
13 changes: 12 additions & 1 deletion src/configs/types/GameServersConfig.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
export const PLUGIN_RUNTIMES = ["swiftlys2", "counterstrikesharp"] as const;

export type PluginRuntime = (typeof PLUGIN_RUNTIMES)[number];

export const DEFAULT_PLUGIN_RUNTIME: PluginRuntime = "swiftlys2";

export const isPluginRuntime = (
value?: string | null,
): value is PluginRuntime => PLUGIN_RUNTIMES.includes(value as PluginRuntime);

export type GameServersConfig = {
namespace: string;
serverImage: string;
serverImageOverride: string | null;
pluginRuntimeImages: Record<PluginRuntime, string>;
gameStreamerImage: string;
};
2 changes: 2 additions & 0 deletions src/dedicated-servers/dedicated-servers.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { getQueuesProcessors } from "src/utilities/QueueProcessors";
import { RconModule } from "src/rcon/rcon.module";
import { RedisModule } from "src/redis/redis.module";
import { SystemModule } from "src/system/system.module";
import { PluginRuntimeModule } from "src/plugin-runtime/plugin-runtime.module";

@Module({
imports: [
Expand All @@ -29,6 +30,7 @@ import { SystemModule } from "src/system/system.module";
RconModule,
RedisModule,
SystemModule,
PluginRuntimeModule,
],
providers: [
DedicatedServersService,
Expand Down
35 changes: 27 additions & 8 deletions src/dedicated-servers/dedicated-servers.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { RconService } from "src/rcon/rcon.service";
import { RedisManagerService } from "src/redis/redis-manager/redis-manager.service";
import { Redis } from "ioredis";
import { SystemService } from "src/system/system.service";
import { PluginRuntimeService } from "src/plugin-runtime/plugin-runtime.service";

@Injectable()
export class DedicatedServersService {
Expand All @@ -30,6 +31,7 @@ export class DedicatedServersService {
private readonly RconService: RconService,
private readonly redisManager: RedisManagerService,
private readonly systemService: SystemService,
private readonly pluginRuntimeService: PluginRuntimeService,
) {
this.redis = this.redisManager.getConnection();

Expand Down Expand Up @@ -66,6 +68,7 @@ export class DedicatedServersService {
game_server_node: {
id: true,
pin_plugin_version: true,
pin_plugin_runtime: true,
supports_cpu_pinning: true,
},
server_region: {
Expand Down Expand Up @@ -126,16 +129,32 @@ export class DedicatedServersService {
? `serverfiles-csgo-${sanitizedGameServerNodeId}`
: `serverfiles-${sanitizedGameServerNodeId}`;

let pluginImage = this.gameServerConfig.serverImage;

const pinPluginVersion = server.game_server_node?.pin_plugin_version;
const pluginRuntime =
await this.pluginRuntimeService.resolvePluginRuntime(
server.game_server_node,
);

if (pinPluginVersion) {
pluginImage = this.gameServerConfig.serverImage.replace(
/:.+$/,
`:v${pinPluginVersion.toString()}`,
const pluginImage =
await this.pluginRuntimeService.resolveGameServerPluginImage(
server.game_server_node,
pluginRuntime,
);
}

// Seeded here so out-of-date checks know the framework even before the
// plugin's first ping; the ping overwrites it with what actually loaded.
await this.hasura.mutation({
update_servers_by_pk: {
__args: {
pk_columns: {
id: serverId,
},
_set: {
plugin_runtime: pluginRuntime,
},
},
__typename: true,
},
});

const dedicatedServerDeploymentName =
this.getDedicatedServerDeploymentName(serverId);
Expand Down
Loading
Loading