build config changes
All checks were successful
Galvanic Corrosion Cross-Compile / build (push) Successful in 1m50s

* Commit hash shipped with builds
* Post & pre-build events
* Objective fixes
* Orientation challenge filler
* Custom Rooms base
    - Currently cannot save rooms (CDN not set up)
* Moved root path to path.ts
* Room cloning
* Rewrote instances - the whole thing
* Relationships are still untested
* Charades Words
* AG Room fetch
* Private room matchmaking
* Socket fixes
This commit is contained in:
2025-05-12 09:07:59 -04:00
parent 6a249ef813
commit 83440a9245
96 changed files with 1201 additions and 436 deletions

View File

@@ -623,7 +623,7 @@ copy of the Program in return for a fee.
Galvanic Corrosion - Rec Room custom server for communities. Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,19 +1,18 @@
# Galvanic Corrosion # Galvanic Corrosion
delectable yum yum delectable yum yum<br>
Rec Room custom server for communities. Fast runtime and easy setup.<br>Built Rec Room custom server for communities. Fast runtime and easy setup.<br>Built
for Rec Room build 1063 (Timestamp: 637191339113673856, Version: 20200306) for Rec Room build 1063 (Timestamp: 637191339113673856, Version: 20200306)
<img src="galv4.jpg" alt="drawing" width="200"/> <img src="galv4.jpg" alt="drawing" width="200"/><br>
Photo taken by Nick Gromicko, CMI®
# Disclaimer # Disclaimer
Galvanic Corrosion and its contributors are **not** associated in **any** form with: Galvanic Corrosion and its contributors are **not** associated in **any** form with:
* RecNet ("the RecNet platform")
* Rec Room, Inc. * Rec Room, Inc.
* Exit Games Inc.
* Against Gravity * Against Gravity
* Photon Network, Photon Engine, or services associated * Exit Games Inc.
* Any person(s) in contact with or employed for or with Rec Room Inc. * Any person(s) in contact with or employed for or with Rec Room Inc.
## Configuration ## Configuration

View File

@@ -1,9 +1,13 @@
{ {
"tasks": { "tasks": {
"compile-win": "deno compile --include res --include src --target x86_64-pc-windows-msvc -o build/GalvanicCorrosion.exe -A src/main.ts", "compile-win-a": "deno compile --include res --include src --target x86_64-pc-windows-msvc -o build/GalvanicCorrosion.exe -A src/main.ts",
"compile-linux": "deno compile --include res --include src --target x86_64-unknown-linux-gnu -o build/GalvanicCorrosion -A src/main.ts", "compile-linux-a": "deno compile --include res --include src --target x86_64-unknown-linux-gnu -o build/GalvanicCorrosion -A src/main.ts",
"cross-compile": "deno run compile-win && deno run compile-linux", "compile-win": "deno run prebuild && deno run compile-win-a && deno run postbuild",
"dev": "deno run -A src/main.ts --dev" "compile-linux": "deno run prebuild && deno run compile-linux-a && deno run postbuild",
"cross-compile": "deno run prebuild && deno run compile-win-a && deno run compile-linux-a && deno run postbuild",
"dev": "deno run -A src/main.ts --dev",
"prebuild": "deno run -A ./prebuild.ts",
"postbuild": "deno run -A ./postbuild.ts"
}, },
"imports": { "imports": {
"@gz/jwt": "jsr:@gz/jwt@^0.1.0", "@gz/jwt": "jsr:@gz/jwt@^0.1.0",
@@ -28,5 +32,6 @@
"./src/types/express.ts", "./src/types/express.ts",
"./src/types/http.ts" "./src/types/http.ts"
] ]
} },
"version": "0.1.0"
} }

24
postbuild.ts Normal file
View File

@@ -0,0 +1,24 @@
/* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
try {
Deno.removeSync('./ver.ts');
Deno.renameSync('./ver.ts.bak', 'ver.ts');
} catch (err) {
console.error(`Cannot post-build version information: ${err}`);
Deno.exit(1);
}

38
prebuild.ts Normal file
View File

@@ -0,0 +1,38 @@
/* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
interface DenoProj {
version: string
}
try {
const file = JSON.parse(Deno.readTextFileSync('./deno.json').toString()) as DenoProj;
const devVer = Deno.readTextFileSync('./ver.ts');
const commitHash = new Deno.Command("git", { args: ["rev-parse", "--short=12", "HEAD"] }).outputSync();
const newVerString = `${file.version}-${new TextDecoder().decode(commitHash.stdout).trim()}`;
if (file.version) {
Deno.writeTextFileSync('./ver.ts.bak', devVer);
Deno.writeTextFileSync('./ver.ts', devVer.replace('development', newVerString));
console.info('Built version information');
}
} catch (err) {
console.error(`Cannot build version information: ${err}`);
Deno.exit(1);
}

262
res/words.json Normal file
View File

@@ -0,0 +1,262 @@
{
"easy": [
"Bike",
"Light",
"Jacket",
"Butterfly",
"Orange",
"Triangle",
"Lemon",
"Crab",
"Alligator",
"Shoe",
"Purse",
"Leg",
"Neck",
"Starfish",
"Box",
"Backpack",
"House",
"Duck",
"Skateboard",
"Cupcake",
"Hamburger",
"Owl",
"Cup",
"Hand",
"Smile",
"Curl",
"Hippo",
"Turtle",
"Truck",
"Beach",
"Ocean",
"Pants",
"Lips",
"Drum",
"Dragon",
"Egg",
"Fish",
"Desk",
"Mouth",
"Feather",
"Grapes",
"Zoo",
"Bathroom",
"Bus",
"Shirt",
"Grass",
"Snail",
"Ice Cream Cone",
"Snake",
"Arm",
"Cookie",
"Table",
"Dinosaur",
"Dream",
"Frog",
"Ant",
"Sheep",
"Boat",
"Baby",
"Bowl",
"Banana",
"Pig",
"Dog",
"Ants",
"Corn",
"Coat",
"Slide",
"Comb",
"Bug",
"Pizza",
"Plant",
"Pencil",
"Key",
"Cloud",
"Lamp",
"Balloon",
"Robot",
"Chimney",
"Motorcycle",
"Bounce",
"Square",
"Pie",
"Swimming Pool",
"Bumblebee",
"Flower",
"Lollipop",
"Bird",
"King",
"Jellyfish",
"Bone",
"Island",
"Moon",
"Seashell",
"Nail",
"Bunk Bed",
"Jail",
"Ring",
"Family",
"Airplane",
"Earth",
"Hair",
"Snowman",
"Car",
"Hook",
"Sea",
"Pen",
"Diamond",
"Spoon",
"Kite",
"Woman",
"Socks",
"Mouse",
"Cube",
"Finger",
"Lizard",
"Angel",
"Mickey Mouse",
"Spider",
"Mountain",
"Branch",
"Spider Web",
"Ship",
"Bunny",
"Face",
"Music",
"Bell",
"Rainbow",
"Star"
],
"hard": [
"Parent",
"Concession Stand",
"Tablespoon",
"Puppet",
"Gold",
"Mirror",
"Nanny",
"Irrigation",
"Humidity",
"Postcard",
"Musician",
"Chairman",
"Human",
"Barber",
"Judge",
"Jedi",
"Customer",
"Fur",
"Punk",
"Elf",
"Sled",
"Vitamin",
"Print",
"Internet",
"Swarm",
"Zoo",
"Shelter",
"Blizzard",
"Dust Bunny",
"Printer Ink",
"Oxcart",
"Carat",
"Black Belt",
"Fireman Pole",
"Darkness",
"Carpenter",
"Coach",
"Back Flip",
"Extension Cord",
"Flu",
"Telephone Booth",
"Country",
"Sticky Note",
"Traffic Jam",
"Science",
"Wooly Mammoth",
"Captain",
"Driveway",
"Mysterious",
"Dew",
"Injury",
"Sunburn",
"Grandpa",
"Ceiling Fan",
"Cruise",
"Chime",
"Accounting",
"Hour",
"End Zone",
"Miner",
"Government",
"Lunar Rover",
"Pro",
"Date",
"Police",
"Fireside",
"Hand Soap",
"Ditch",
"Expert",
"Fade",
"Nap",
"Last",
"Hot Tub",
"Loveseat",
"Braid",
"Rodeo",
"Coastline",
"Molar",
"Drugstore",
"Tow Truck",
"Pickup Truck",
"Shrink Ray",
"Crane",
"Bedbug",
"Beluga Whale",
"Fiance",
"Fizz",
"Junk",
"Yolk",
"Photosynthesis",
"Wobble",
"Hairspray",
"Crop Duster",
"Recycle",
"Script",
"Vein",
"Jaw",
"Thaw",
"Goalkeeper",
"Optometrist",
"Shower Curtain",
"Cowboy",
"Tide",
"Quadrant",
"Bride",
"Pile",
"Midnight",
"Amusement Park",
"Orbit",
"Goblin",
"Bonnet",
"Snore",
"Cape",
"Truck Stop",
"Ginger",
"Hermit Crab",
"Safe",
"Jungle",
"Student",
"Headache",
"Scuba Diving",
"Neighborhood",
"Bookend",
"Crow's Nest",
"Plantation",
"Owner",
"Stuffed Animal",
"Gown"
]
}

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { Config } from "../config.ts"; import { Config } from "../config.ts";
import { Redis } from "../db.ts"; import { Redis } from "../db.ts";
import { Objectives } from "./objectives.ts"; import { Objectives, ObjectiveType } from "./objectives.ts";
export type LevelProgressionItem = { export type LevelProgressionItem = {
Level: number; Level: number;
@@ -67,7 +67,43 @@ export function getConfig() {
StartsInMinutes: 0, StartsInMinutes: 0,
}, },
LevelProgressionMaps: generateLevelProgressionMap(), LevelProgressionMaps: generateLevelProgressionMap(),
DailyObjectives: [[{type: -1,score:0},{type: -1,score:0},{type: -1,score:0}],[{type: -1,score:0},{type: -1,score:0},{type: -1,score:0}],[{type: -1,score:0},{type: -1,score:0},{type: -1,score:0}]], DailyObjectives: [
[
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0}
],
[
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0}
],
[
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0}
],
[
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0}
],
[
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0}
],
[
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0}
],
[
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0},
{type: ObjectiveType.Default, score: 0}
]
],
AutoMicMutingConfig: { AutoMicMutingConfig: {
MicSpamVolumeThreshold: 1.125, MicSpamVolumeThreshold: 1.125,
MicVolumeSampleInterval: 0.25, MicVolumeSampleInterval: 0.25,

View File

@@ -0,0 +1,47 @@
/* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
import Logging from "@proxnet/undead-logging";
import { RootPath } from "../../path.ts";
const log = new Logging("Activities");
enum CharadesWordsDifficulty {
Easy,
Hard
}
interface CharadesWord {
EN_US: string,
Difficulty: CharadesWordsDifficulty
}
interface WordsConfig {
easy: string[],
hard: string[]
}
let charades: WordsConfig | undefined;
try {
const data = Deno.readTextFileSync(`${RootPath}/res/words.json`);
charades = JSON.parse(data);
} catch (err) {
log.e(`Could not read charades words config from disk!`);
}
export function getWords() {
if (!charades) return [{EN_US: "Galvanic Corrosion", Difficulty: CharadesWordsDifficulty.Easy}] as CharadesWord[]
else return charades.easy.map(val => ({EN_US: val, Difficulty: CharadesWordsDifficulty.Easy} as CharadesWord))
.concat(charades.hard.map(val => ({EN_US: val, Difficulty: CharadesWordsDifficulty.Hard} as CharadesWord)))
}

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -15,9 +15,7 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { platform } from "node:process"; import { RootPath } from "../../path.ts";
export const RootPath = Deno.mainModule.substring(platform == 'win32' ? 8 : 7, Deno.mainModule.length - 11);
export function getBaseImage(name: string) { export function getBaseImage(name: string) {
try { try {
@@ -31,4 +29,4 @@ export function getAllBaseImages() {
Deno.readDirSync(`${RootPath}/res/img/`).map((val) => val.isFile ? val.name : undefined), Deno.readDirSync(`${RootPath}/res/img/`).map((val) => val.isFile ? val.name : undefined),
).filter((val) => typeof val == "string"); ).filter((val) => typeof val == "string");
} }
// todo: make this async // todo: make this async

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,94 +0,0 @@
/* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { Buffer } from "node:buffer";
import { Redis } from "../../db.ts";
import { generateRandomString } from "../../apiutils.ts";
export enum FileType {
Unknown,
RoomSave,
Holotar,
Image,
Video,
Invention
}
export function getFileName(prefix: string, type: FileType) {
switch (type) {
case FileType.RoomSave:
return `${prefix}.room`;
case FileType.Holotar:
return `${prefix}.holotar`;
case FileType.Image:
return `${prefix}.image`;
case FileType.Video:
return `${prefix}.video`;
case FileType.Invention:
return `${prefix}.invention`;
default:
return `${prefix}.unknown`
}
}
export async function getFile(name: string) {
const data = await Redis.Database.getBuffer(Redis.buildKey(
Redis.KeyGroups.Content.Root,
name
));
if (!data) return null;
else return data;
}
/**
* @returns Name of the new file
*/
export async function setFile(data: Buffer<ArrayBufferLike>, type: FileType, name?: string) {
let filename = generateRandomString(24);
if (name) filename = name;
const finalName = getFileName(filename, type);
await Redis.Database.set(Redis.buildKey(
Redis.KeyGroups.Content.Root,
finalName
), data);
await initFileMeta(filename, type);
return filename;
}
interface FileMeta {
created: Date | string;
fetchCount: number;
type: FileType
}
async function initFileMeta(filename: string, type: FileType) {
const meta: FileMeta = {
created: new Date(),
fetchCount: 0,
type: type
}
await Redis.Database.hset(Redis.buildKey(
Redis.KeyGroups.Content.Files,
getFileName(filename, type)
), meta);
}
export async function incrementFileFetches(name: string, type: FileType) {
}

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -16,13 +16,12 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { Redis } from "../../db.ts"; import { Redis } from "../../db.ts";
import { RootPath } from "./baseimages.ts";
import { Profile } from "../profiles.ts"; import { Profile } from "../profiles.ts";
import Logging from "@proxnet/undead-logging"; import Logging from "@proxnet/undead-logging";
import { BuiltinRoom, FactoryMode, IntegratedRoomScene, RoomAccessibility, RoomDetails, RoomState, WriteMode } from "./rooms/DataTypes.ts"; import { BuiltinRoom, FactoryMode, IntegratedRoomScene, RoomAccessibility, RoomDataTypes, RoomDetails, RoomState, WriteMode } from "./rooms/DataTypes.ts";
import { RoomFactory } from "./rooms/RoomFactory.ts"; import { RoomFactory } from "./rooms/RoomFactory.ts";
import { SubroomFactory } from "./rooms/SubroomFactory.ts"; import { SubroomFactory } from "./rooms/SubroomFactory.ts";
import { Image } from "https://deno.land/x/imagescript@1.3.0/ImageScript.js"; import { RootPath } from "../../path.ts";
const log = new Logging("Rooms"); const log = new Logging("Rooms");
@@ -87,34 +86,27 @@ class RoomsBase {
} }
async cloneRoom(roomid: number, newname: string, newowner: Profile) { async cloneRoom(roomid: number, newname: string, newowner: Profile) {
enum CloneResult {
Success,
DoesNotAllowCloning,
CannotCloneDormRoom,
NameIsTaken,
Unknown
}
interface RoomClone { interface RoomClone {
factory?: RoomFactory; factory?: RoomFactory;
result: CloneResult; result: RoomDataTypes.CreateModifyRoomStatus;
} }
const factory = await new RoomFactory({ id: roomid, factoryMode: FactoryMode.Fetch }).init(); const factory = await new RoomFactory({ id: roomid, factoryMode: FactoryMode.Fetch }).init();
if (!factory || !factory.CloningAllowed) return { result: CloneResult.DoesNotAllowCloning } as RoomClone; if (!factory || !factory.CloningAllowed) return { result: RoomDataTypes.CreateModifyRoomStatus.PermissionDenied } as RoomClone;
if (factory.Name == 'DormRoom') return { result: CloneResult.CannotCloneDormRoom } as RoomClone; if (factory.Name == 'DormRoom') return { result: RoomDataTypes.CreateModifyRoomStatus.ReservedName } as RoomClone;
if (factory.Name == newname) return { result: CloneResult.NameIsTaken } as RoomClone; if (factory.Name == newname) return { result: RoomDataTypes.CreateModifyRoomStatus.DuplicateName } as RoomClone;
const newFactory = await new RoomFactory({ id: await Rooms.#getAvailableRoomId(), factoryMode: FactoryMode.Write }).init(); const newFactory = await new RoomFactory({ id: await Rooms.#getAvailableRoomId(), factoryMode: FactoryMode.Write }).init();
if (!newFactory) return { result: CloneResult.Unknown } as RoomClone; if (!newFactory) return { result: RoomDataTypes.CreateModifyRoomStatus.Unknown } as RoomClone;
newFactory.CreatorPlayerId = newowner.getId(); newFactory.CreatorPlayerId = newowner.getId();
newFactory.Description = factory.Description; newFactory.Description = factory.Description;
newFactory.Name = factory.Name; newFactory.Name = newname;
newFactory.ImageName = factory.Description; newFactory.ImageName = factory.ImageName;
newFactory.State = factory.State; newFactory.State = RoomState.Active;
newFactory.RoomAccessibility = factory.RoomAccessibility; newFactory.RoomAccessibility = RoomAccessibility.Private;
newFactory.SupportsLevelVoting = factory.SupportsLevelVoting; newFactory.SupportsLevelVoting = factory.SupportsLevelVoting;
newFactory.IsAGRoom = factory.IsAGRoom; newFactory.IsAGRoom = false;
newFactory.IsDormRoom = factory.IsDormRoom; newFactory.IsDormRoom = factory.IsDormRoom;
newFactory.CloningAllowed = false; // new rooms cannot be cloned newFactory.CloningAllowed = false; // new rooms cannot be cloned
newFactory.AllowsJuniors = factory.AllowsJuniors; newFactory.AllowsJuniors = factory.AllowsJuniors;
@@ -128,8 +120,8 @@ class RoomsBase {
const oldSubroomIds = await factory.getAllSubroomIds(); const oldSubroomIds = await factory.getAllSubroomIds();
const promises = oldSubroomIds.map(async (id) => { const promises = oldSubroomIds.map(async (id) => {
const newSubroomFactory = newFactory.getSubroom(id, FactoryMode.Write, WriteMode.Overwrite); const newSubroomFactory = await newFactory.getSubroom(id, FactoryMode.Write, WriteMode.Overwrite).init();
const oldSubroomFactory = factory.getSubroom(id, FactoryMode.Fetch); const oldSubroomFactory = await factory.getSubroom(id, FactoryMode.Fetch).init();
newSubroomFactory.RoomSceneLocationId = oldSubroomFactory.RoomSceneLocationId; newSubroomFactory.RoomSceneLocationId = oldSubroomFactory.RoomSceneLocationId;
newSubroomFactory.Name = oldSubroomFactory.Name; newSubroomFactory.Name = oldSubroomFactory.Name;
@@ -143,10 +135,11 @@ class RoomsBase {
await Promise.all(promises); await Promise.all(promises);
await newFactory.write(); await newFactory.write();
newFactory.factoryMode = FactoryMode.Fetch;
return { return {
factory: newFactory, factory: newFactory,
result: CloneResult.Success result: RoomDataTypes.CreateModifyRoomStatus.Success
} as RoomClone } as RoomClone
} }
@@ -199,8 +192,8 @@ class RoomsBase {
factory.CustomRoomWarning = ""; factory.CustomRoomWarning = "";
factory.addHardwareSupport('*'); factory.addHardwareSupport('*');
const subroomFactory = factory.getSubroom(await this.#getAvailableSubRoomId(id), FactoryMode.Write, WriteMode.WriteIfFree); const subroomFactory = await factory.getSubroom(await this.#getAvailableSubRoomId(id), FactoryMode.Write, WriteMode.WriteIfFree).init();
if (!subroomFactory) return null; if (!subroomFactory) return null;
subroomFactory.RoomSceneLocationId = IntegratedRoomScene.DormRoom; subroomFactory.RoomSceneLocationId = IntegratedRoomScene.DormRoom;
@@ -226,7 +219,7 @@ class RoomsBase {
const factory = await new RoomFactory({ id: newId, factoryMode: FactoryMode.Write, writeMode: WriteMode.Overwrite }).init(); const factory = await new RoomFactory({ id: newId, factoryMode: FactoryMode.Write, writeMode: WriteMode.Overwrite }).init();
if (!factory) return; if (!factory) return;
factory.Name = builtinRoom.Name; factory.Name = builtinRoom.Name;
factory.Description = builtinRoom.Description; factory.Description = builtinRoom.Description;
factory.CreatorPlayerId = 1; factory.CreatorPlayerId = 1;
@@ -268,7 +261,7 @@ class RoomsBase {
subroomFactory.DataBlobName = ""; subroomFactory.DataBlobName = "";
subroomFactory.MaxPlayers = subroom.MaxPlayers; subroomFactory.MaxPlayers = subroom.MaxPlayers;
subroomFactory.CanMatchmakeInto = subroom.CanMatchmakeInto; subroomFactory.CanMatchmakeInto = subroom.CanMatchmakeInto;
await subroomFactory.write(); await subroomFactory.write();
})); }));

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -37,9 +37,9 @@ export enum RoomState {
} }
export enum RoomAccessibility { export enum RoomAccessibility {
Private, Private,
Public, Public,
Unlisted Unlisted
} }
export interface BuiltinScene { export interface BuiltinScene {
@@ -143,6 +143,7 @@ export enum IntegratedRoomScene {
DiscGolfLake = "f6f7256c-e438-4299-b99e-d20bef8cf7e0", DiscGolfLake = "f6f7256c-e438-4299-b99e-d20bef8cf7e0",
DiscGolfPropulsion = "d9378c9f-80bc-46fb-ad1e-1bed8a674f55", DiscGolfPropulsion = "d9378c9f-80bc-46fb-ad1e-1bed8a674f55",
Dodgeball = "3d474b26-26f7-45e9-9a36-9b02847d5e6f", Dodgeball = "3d474b26-26f7-45e9-9a36-9b02847d5e6f",
DodgeballVR = "3d474b26-26f7-45e9-9a36-9b02847d5e6f",
Paddleball = "d89f74fa-d51e-477a-a425-025a891dd499", Paddleball = "d89f74fa-d51e-477a-a425-025a891dd499",
Paintball_River = "e122fe98-e7db-49e8-a1b1-105424b6e1f0", Paintball_River = "e122fe98-e7db-49e8-a1b1-105424b6e1f0",
Paintball_Homestead = "a785267d-c579-42ea-be43-fec1992d1ca7", Paintball_Homestead = "a785267d-c579-42ea-be43-fec1992d1ca7",
@@ -179,7 +180,6 @@ export enum IntegratedRoomScene {
Stadium = "6d5eea4b-f069-4ed0-9916-0e2f07df0d03", Stadium = "6d5eea4b-f069-4ed0-9916-0e2f07df0d03",
Hangar = "239e676c-f12f-489f-bf3a-d4c383d692c3", Hangar = "239e676c-f12f-489f-bf3a-d4c383d692c3",
CyberJunkCity = "9d6456ce-6264-48b4-808d-2d96b3d91038", CyberJunkCity = "9d6456ce-6264-48b4-808d-2d96b3d91038",
DodgeballVR = "3d474b26-26f7-45e9-9a36-9b02847d5e6f",
Crescendo = "49cb8993-a956-43e2-86f4-1318f279b22a", Crescendo = "49cb8993-a956-43e2-86f4-1318f279b22a",
Bowling = "ae929543-9a07-41d5-8ee9-dbbee8c36800", Bowling = "ae929543-9a07-41d5-8ee9-dbbee8c36800",
BowlingAlley = "ae929543-9a07-41d5-8ee9-dbbee8c36800", BowlingAlley = "ae929543-9a07-41d5-8ee9-dbbee8c36800",
@@ -188,4 +188,31 @@ export enum IntegratedRoomScene {
StuntRunnerBaseRoom = "882e9b96-7115-4b03-86f6-c0c9d8e22e00", StuntRunnerBaseRoom = "882e9b96-7115-4b03-86f6-c0c9d8e22e00",
} }
export enum CreateModifyRoomStatus {
Success,
Unknown,
PermissionDenied,
RoomNotActive,
RoomDoesNotExist,
RoomHasNoDataBlob,
DuplicateName = 10,
ReservedName,
InappropriateName,
InappropriateDescription,
TooManyRooms = 20,
InvalidMaxPlayers = 30,
DataHistoryDoesNotExist = 40,
DataHistoryAlreadyActive,
InvalidTags = 50,
NoStartingRoomScene = 55,
RoomUnderModerationReview = 100,
PlayerHasRoomUnderModerationReview,
AccessibilityUnderModerationLock,
JuniorStatusFail = 200,
InappropriateCustomWarning = 300,
PartialBanSuccessBanPower = 400,
TargetHasBanPower,
PlayerIsRoomBanned = 410
}
export * as RoomDataTypes from "./DataTypes.ts"; export * as RoomDataTypes from "./DataTypes.ts";

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -60,14 +60,14 @@ class MatchmakingBase {
const instance = Instances.getInstance(options.instanceId); const instance = Instances.getInstance(options.instanceId);
if (instance) { if (instance) {
if (Instances.playerIsInInstance(options.profile, instance)) return { errorCode: MatchmakingErrorCode.AlreadyInTargetInstance }; if (instance.hasPlayer(options.profile)) return { errorCode: MatchmakingErrorCode.AlreadyInTargetInstance };
else { else {
if (instance.isFull) return { errorCode: MatchmakingErrorCode.InsufficientSpace } if (instance.isFull) return { errorCode: MatchmakingErrorCode.InsufficientSpace }
else if (instance.isPrivate) return { errorCode: MatchmakingErrorCode.RoomInstanceIsPrivate }; else if (instance.isPrivate) return { errorCode: MatchmakingErrorCode.RoomInstanceIsPrivate };
await Instances.setPlayerInstance(options.profile, instance); await instance.addPlayer(options.profile);
Instances.clearAllRoomEmptyInstances(instance.roomId); Instances.clearEmptyInstances(instance.roomId);
return { errorCode: MatchmakingErrorCode.Success, roomInstance: instance }; return { errorCode: MatchmakingErrorCode.Success, roomInstance: instance.snapshot() };
} }
} else return { errorCode: MatchmakingErrorCode.NoSuchGame } } else return { errorCode: MatchmakingErrorCode.NoSuchGame }
@@ -82,7 +82,7 @@ class MatchmakingBase {
if (targetRoom.Room.State !== RoomDataTypes.RoomState.Active) return { errorCode: MatchmakingErrorCode.RoomIsNotActive }; if (targetRoom.Room.State !== RoomDataTypes.RoomState.Active) return { errorCode: MatchmakingErrorCode.RoomIsNotActive };
const roomId = targetRoom.Room.RoomId; const roomId = targetRoom.Room.RoomId;
Instances.clearAllRoomEmptyInstances(roomId); Instances.clearEmptyInstances(roomId);
// get all available instance // get all available instance
let allInstances = Instances.getAllRoomInstances(roomId).values().toArray().filter(instance => !instance.isPrivate && !instance.isFull); let allInstances = Instances.getAllRoomInstances(roomId).values().toArray().filter(instance => !instance.isPrivate && !instance.isFull);
@@ -108,9 +108,9 @@ class MatchmakingBase {
}); });
allInstances = allInstances.sort((a, b) => { allInstances = allInstances.sort((a, b) => {
const pidsA = Instances.getInstancePlayers(a); const pidsA = a.getAllPlayers();
const pidsB = Instances.getInstancePlayers(b); const pidsB = b.getAllPlayers();
return pidsA.size - pidsB.size; return pidsA.length - pidsB.length;
}).reverse(); // Largest instances first }).reverse(); // Largest instances first
const foundInstance = allInstances[0]; const foundInstance = allInstances[0];
@@ -125,8 +125,8 @@ class MatchmakingBase {
IsDorm: options.roomName == 'DormRoom' IsDorm: options.roomName == 'DormRoom'
}); });
Instances.clearAllRoomEmptyInstances(roomId); Instances.clearEmptyInstances(roomId);
return { errorCode: MatchmakingErrorCode.Success, roomInstance: newInstance }; return { errorCode: MatchmakingErrorCode.Success, roomInstance: newInstance.snapshot() };
} else { } else {
@@ -134,11 +134,11 @@ class MatchmakingBase {
if (currentInstance?.roomInstanceId == foundInstance.roomInstanceId) if (currentInstance?.roomInstanceId == foundInstance.roomInstanceId)
return { errorCode: MatchmakingErrorCode.AlreadyInBestInstance }; return { errorCode: MatchmakingErrorCode.AlreadyInBestInstance };
await Instances.setPlayerInstance(options.profile, foundInstance); await foundInstance.addPlayer(options.profile);
Instances.clearAllRoomEmptyInstances(roomId); Instances.clearEmptyInstances(roomId);
return { errorCode: MatchmakingErrorCode.Success, roomInstance: foundInstance }; return { errorCode: MatchmakingErrorCode.Success, roomInstance: foundInstance.snapshot() };
} }

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -21,84 +21,191 @@ import { RoomInstance, InstanceOptions } from "./types.ts";
import { Config } from "../../config.ts"; import { Config } from "../../config.ts";
import Presence from "./presence.ts"; import Presence from "./presence.ts";
import { RoomFactory } from "../content/rooms/RoomFactory.ts"; import { RoomFactory } from "../content/rooms/RoomFactory.ts";
import { RoomDataTypes } from "../content/rooms/DataTypes.ts";
import { PushNotificationId } from "../../socket/types.ts";
const log = new Logging("Instances"); const log = new Logging("Instances");
const config = Config.getConfig(); const config = Config.getConfig();
const instancePlayers: Map<RoomInstance, Set<Profile>> = new Map();
/** /**
* `Map<roomId (number), RoomInstance>` * `Map<roomId (number), Instance>`
*/ */
const instanceMap: Map<number, Set<RoomInstance>> = new Map(); const instanceSet: Set<Instance> = new Set();
export class Instance {
#players = new Set<Profile>();
timeCreated = new Date().toISOString();
#id: number;
#room: RoomDataTypes.RoomDetails | undefined;
#subroom: RoomDataTypes.RoomScene | undefined;
#eventId?: number; // not yet implemented
#name?: string;
#priv?: boolean;
#inProgress?: boolean;
#blob?: string;
constructor(id: number) {
this.#id = id;
}
async init(options: InstanceOptions) {
const scene = options.Room.Scenes[options.SceneIndex];
if (!scene) throw new Error("The specified scene did not exist.");
let instanceName;
if (scene.Name == 'Home' || scene.Name === options.Room.Room.Name) instanceName = `^${options.Room.Room.Name}`;
else instanceName = `^${options.Room.Room.Name}.${scene.Name}`;
if (options.IsDorm) {
const dormCreatorPlayer = UnifiedProfile.get(options.Room.Room.CreatorPlayerId);
const player = await dormCreatorPlayer.export();
if (player) instanceName = `@${player.displayName}'s Dorm`;
}
this.#room = options.Room;
this.#subroom = scene;
this.#name = instanceName;
this.#blob = scene.DataBlobName;
this.#inProgress = false;
this.#priv = options.Private ? options.Private : false;
return this;
}
equalInstance(instance: RoomInstance) {
return instance.roomInstanceId == this.#id;
}
getAllPlayers() {
return this.#players.values().toArray();
}
hasPlayer(player: Profile) {
return this.getAllPlayers().includes(player);
}
removePlayer(player: Profile) {
if (!this.hasPlayer(player)) throw new Error(`Cannot remove player ${player.getId()} from instance ${this.#id} they are not in`);
this.#players.delete(player);
player.setInstance(null);
}
async addPlayer(player: Profile) {
const currentInstance = player.getInstance();
if (currentInstance && currentInstance.equalInstance(this)) return;
if (currentInstance) currentInstance.removePlayer(player);
if (!this.isFull) {
const instancePlayers = this.getAllPlayers();
const profileExport = await player.export();
log.i(`Player ${player.getId()} "${profileExport?.displayName}" went to '${this.name}' with ${instancePlayers.length} other players`);
this.#players.add(player);
player.setInstance(this);
const pres = await Presence.get(player);
pres.update();
const room = await new RoomFactory({ id: this.roomId }).init();
await room?.addVisit();
// move some of this to a dedicated "onPlayerMove" function
} else log.w(`Instance ${this.roomInstanceId} is full. Cannot add player ${player.getId()}`);
log.d(`Players in instance ${this.#id}: ${this.#players.values().toArray().map(prof => prof.getId()).join(',')}`);
}
get roomInstanceId() { return this.#id }
get roomId() { return this.#room ? this.#room?.Room.RoomId : 0 }
get subRoomId() { return this.#subroom ? this.#subroom?.RoomSceneId : 0 }
get location() { return this.#subroom ? this.#subroom?.RoomSceneLocationId : "" }
get dataBlob() { return this.#blob ? this.#blob : undefined }
set dataBlob(data) { this.#blob = data }
get eventId() { return this.#eventId }
get photonRegionId() { return config.public.photonRegionId }
get photonRoomId() { return `GC20200306-${this.#id}` }
get name() { return this.#name ? this.#name : "InstanceNameError" }
get maxCapacity() { return this.#subroom ? this.#subroom.MaxPlayers : 8 }
get isFull() { return this.#players.size >= this.maxCapacity }
get isPrivate() { return this.#priv ? this.#priv : false }
set isPrivate(data) { this.#priv = data }
get isInProgress() { return this.#inProgress ? this.#inProgress : false }
set isInProgress(data) { this.#inProgress = data }
snapshot() {
const inst: RoomInstance = {
roomInstanceId: this.roomInstanceId,
roomId: this.roomId,
subRoomId: this.subRoomId,
location: this.location,
dataBlob: this.dataBlob,
eventId: this.eventId,
photonRegionId: this.photonRegionId,
photonRoomId: this.photonRoomId,
name: this.name,
maxCapacity: this.maxCapacity,
isFull: this.isFull,
isPrivate: this.isPrivate,
isInProgress: this.isInProgress
}
return inst;
}
destroy() {
instanceSet.delete(this);
if (this.#players.size !== 0) for (const player of this.#players) player.getSocketHandler()?.sendNotification(PushNotificationId.Logout);
}
}
class InstancesBase { class InstancesBase {
getInstance(id: number) { getInstance(id: number) {
const instances = instancePlayers.keys(); const instances = instanceSet.values().toArray();
const instance = instances.find(val => val.roomInstanceId == id); const instance = instances.find(val => val.roomInstanceId == id);
if (instance) return instance; if (instance) return instance;
else return null; else return null;
} }
getAllInstances() { getAllInstances() {
return new Set([...instanceMap.values()].flatMap(set => [...set])); return new Set([...instanceSet.values().toArray()]);
} }
getAllRoomInstances(roomId: number) { getAllRoomInstances(roomId: number) {
let instances = instanceMap.get(roomId); return new Set([...this.getAllInstances().values().toArray().filter(val => val.roomId == roomId)]);
if (!instances) {
instances = new Set();
instanceMap.set(roomId, instances);
}
return instances;
}
getInstancePlayers(instance: RoomInstance): Set<Profile> {
let players = instancePlayers.get(instance);
if (!players) {
players = new Set();
instancePlayers.set(instance, players);
}
return players;
} }
clearEmptyInstances(instances: Set<RoomInstance>, roomId?: number) { clearEmptyInstances(roomId?: number) {
const beforeCount = instances.size; const beforeCount = instanceSet.size;
for (const instance of instances) { for (const instance of instanceSet) {
const profiles = this.getInstancePlayers(instance); if (roomId && instance.roomId == roomId) continue;
if (profiles.size === 0) { const profiles = instance.getAllPlayers();
if (profiles.length === 0) {
log.i(`Instance ${instance.roomInstanceId} empty, deleting`); log.i(`Instance ${instance.roomInstanceId} empty, deleting`);
instancePlayers.delete(instance); instance.destroy();
this.getAllRoomInstances(instance.roomId).delete(instance);
} }
} }
const afterCount = instances.size; const afterCount = instanceSet.size;
log.d(`Cleared ${roomId !== undefined ? `room ${roomId}` : 'all'} empty instances.\n Instances before: ${beforeCount}\n Instances after: ${afterCount}`); log.d(`Cleared empty instances for roomId ${roomId ? roomId : "*"}.\n Instances before: ${beforeCount}\n Instances after: ${afterCount}`);
}
clearAllEmptyInstances() {
this.clearEmptyInstances(this.getAllInstances());
}
clearAllRoomEmptyInstances(roomId: number) {
this.clearEmptyInstances(this.getAllRoomInstances(roomId), roomId);
}
updateGlobalInstancesIsFull() {
for (const instance of this.getAllInstances())
instance.isFull = this.getInstancePlayers(instance).size >= instance.maxCapacity;
}
updateSingleInstanceIsFull(instance: RoomInstance) {
const profiles = this.getInstancePlayers(instance);
if (profiles.size >= instance.maxCapacity) instance.isFull = true;
else instance.isFull = false;
}
instanceCanFitPlayer(instance: RoomInstance) {
return this.getInstancePlayers(instance).size < instance.maxCapacity;
} }
#generateUniqueInstanceId() { #generateUniqueInstanceId() {
@@ -112,96 +219,19 @@ class InstancesBase {
* Create an instance with options. * Create an instance with options.
* *
* If `options.FirstPlayer` is not specified, the created instance will not contain any players and may be removed. * If `options.FirstPlayer` is not specified, the created instance will not contain any players and may be removed.
*
* If one is, the player will be automatically added to the instance and their `profile.getInstance()` will be synchronized.
*/ */
async createInstance(options: InstanceOptions) { async createInstance(options: InstanceOptions) {
const scene = options.Room.Scenes[options.SceneIndex];
const newId = this.#generateUniqueInstanceId(); const newId = this.#generateUniqueInstanceId();
if (!scene) throw new Error("The specified scene did not exist."); const newInstance = await new Instance(newId).init(options);
let instanceName = scene.Name === "Home" || scene.Name === options.Room.Room.Name ? `^${options.Room.Room.Name}` : `^${options.Room.Room.Name}.${scene.Name}`; instanceSet.add(newInstance);
if (options.IsDorm) { if (options.FirstPlayer)
const dormCreatorPlayer = UnifiedProfile.get(options.Room.Room.CreatorPlayerId); newInstance.addPlayer(options.FirstPlayer);
const player = await dormCreatorPlayer.export();
if (player) instanceName = `@${player.displayName}'s Dorm`;
}
const newInstance: RoomInstance = {
roomInstanceId: newId,
roomId: options.Room.Room.RoomId,
subRoomId: scene.RoomSceneId,
location: scene.RoomSceneLocationId,
dataBlob: scene.DataBlobName,
eventId: options.EventId,
photonRegionId: config.public.photonRegionId,
photonRoomId: `20200306-GC${newId}`,
name: instanceName,
maxCapacity: scene.MaxPlayers,
isFull: false,
isPrivate: typeof options.Private !== 'boolean' ? false : options.Private,
isInProgress: false
};
this.getAllRoomInstances(options.Room.Room.RoomId).add(newInstance);
if (options.FirstPlayer) {
this.setPlayerInstance(options.FirstPlayer, newInstance);
this.getInstancePlayers(newInstance).add(options.FirstPlayer);
}
return newInstance; return newInstance;
} }
/**
* Call only when the player is ready to be moved to an instance
*
* Synchronizes profile instance to `instance` and adds player to instance.
*/
async setPlayerInstance(player: Profile, instance: RoomInstance) {
const currentInstance = player.getInstance();
if (currentInstance === instance) return;
if (currentInstance) {
this.getInstancePlayers(currentInstance).delete(player);
this.updateSingleInstanceIsFull(currentInstance);
}
if (this.instanceCanFitPlayer(instance)) {
const instancePlayers = this.getInstancePlayers(instance);
log.i(`Player ${player.getId()} went to '${instance.name}' with ${instancePlayers.size} other players`);
instancePlayers.add(player);
player.setInstance(instance);
const pres = await Presence.get(player);
pres.update();
this.updateSingleInstanceIsFull(instance);
} else log.w(`Instance ${instance.roomInstanceId} is full. Cannot add player ${player.getId()}`);
const room = await new RoomFactory({ id: instance.roomId }).init();
await room?.addVisit();
}
playerIsInInstance(player: Profile, instance: RoomInstance) {
const profiles = this.getInstancePlayers(instance);
return profiles.has(player);
}
/**
* Call only when the player is ready to be removed (or when not responding)
*
* Synchronizes profile instance to `null` and removes player from instance.
*/
removePlayerFromCurrentInstance(player: Profile) {
const instance = player.getInstance();
if (!instance) return;
this.getInstancePlayers(instance).delete(player);
player.setInstance(null);
this.updateSingleInstanceIsFull(instance);
}
} }

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -20,6 +20,7 @@ import { SettingKey } from "../content/settings.ts";
import { Profile } from "../profiles.ts"; import { Profile } from "../profiles.ts";
import { DeviceClass, PlayerStatusVisibility, RoomInstance, VRMovementMode } from "./types.ts"; import { DeviceClass, PlayerStatusVisibility, RoomInstance, VRMovementMode } from "./types.ts";
import Logging from "@proxnet/undead-logging"; import Logging from "@proxnet/undead-logging";
import { Instance } from "./instances.ts";
const log = new Logging("Presence"); const log = new Logging("Presence");
@@ -31,6 +32,7 @@ export interface PresenceExport {
vrMovementMode?: VRMovementMode; vrMovementMode?: VRMovementMode;
} }
// This whole class needs rewritten .. probably
class PlayerPresence { class PlayerPresence {
intervalId: number; intervalId: number;
@@ -63,7 +65,7 @@ class PlayerPresence {
statusVisibility: PlayerStatusVisibility; statusVisibility: PlayerStatusVisibility;
deviceClass: DeviceClass; deviceClass: DeviceClass;
vrMovementMode: VRMovementMode | undefined; vrMovementMode: VRMovementMode | undefined;
roomInstance: RoomInstance | null; roomInstance: Instance | null;
lastSeen: Date; lastSeen: Date;
@@ -102,9 +104,10 @@ class PlayerPresence {
*/ */
async export() { async export() {
await this.update(); await this.update();
const inst = this.roomInstance?.snapshot();
const exp: PresenceExport = { const exp: PresenceExport = {
playerId: this.playerId, playerId: this.playerId,
roomInstance: this.roomInstance, roomInstance: inst ? inst : null,
statusVisibility: this.statusVisibility, statusVisibility: this.statusVisibility,
deviceClass: this.deviceClass, deviceClass: this.deviceClass,
vrMovementMode: this.vrMovementMode vrMovementMode: this.vrMovementMode

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -41,6 +41,7 @@ interface Relationship {
Favorited?: ReciprocalStatus Favorited?: ReciprocalStatus
} }
// Literally all of this is untested
export class ProfileRelationshipManager extends ProfileContentManager { export class ProfileRelationshipManager extends ProfileContentManager {
#baseRelationships: Relationship[] = [ #baseRelationships: Relationship[] = [
@@ -123,15 +124,15 @@ export class ProfileRelationshipManager extends ProfileContentManager {
return (await Redis.Database.smembers(this.#friendsKey)).map(val => parseInt(val)).filter(val => !isNaN(val)); return (await Redis.Database.smembers(this.#friendsKey)).map(val => parseInt(val)).filter(val => !isNaN(val));
} }
async #getIncomingRequests() { async getIncomingRequests() {
return (await Redis.Database.smembers(this.#incomingFriends)).map(val => parseInt(val)).filter(val => !isNaN(val)); return (await Redis.Database.smembers(this.#incomingFriends)).map(val => parseInt(val)).filter(val => !isNaN(val));
} }
async #getOutgoingRequests() { async getOutgoingRequests() {
return (await Redis.Database.smembers(this.#outgoingFriends)).map(val => parseInt(val)).filter(val => !isNaN(val)); return (await Redis.Database.smembers(this.#outgoingFriends)).map(val => parseInt(val)).filter(val => !isNaN(val));
} }
createRemoteRootKey(remoteProfileId: number) { #createRemoteRootKey(remoteProfileId: number) {
return Redis.buildKey( return Redis.buildKey(
Redis.KeyGroups.Profiles.Root, Redis.KeyGroups.Profiles.Root,
remoteProfileId.toString(), remoteProfileId.toString(),
@@ -140,7 +141,7 @@ export class ProfileRelationshipManager extends ProfileContentManager {
} }
async #clearAssociationWithRemote(remoteProfileId: number) { async #clearAssociationWithRemote(remoteProfileId: number) {
const remoteRootKey = this.createRemoteRootKey(remoteProfileId); const remoteRootKey = this.#createRemoteRootKey(remoteProfileId);
await Redis.Database.srem(this.#incomingFriends, remoteProfileId); await Redis.Database.srem(this.#incomingFriends, remoteProfileId);
await Redis.Database.srem(Redis.buildKey(remoteRootKey, Redis.KeyGroups.Profiles.Relationships.OutgoingFriendRequests), this.profileId); await Redis.Database.srem(Redis.buildKey(remoteRootKey, Redis.KeyGroups.Profiles.Relationships.OutgoingFriendRequests), this.profileId);
await Redis.Database.srem(Redis.buildKey(remoteRootKey, Redis.KeyGroups.Profiles.Relationships.IncomingFriendRequests), this.profileId); await Redis.Database.srem(Redis.buildKey(remoteRootKey, Redis.KeyGroups.Profiles.Relationships.IncomingFriendRequests), this.profileId);
@@ -148,27 +149,27 @@ export class ProfileRelationshipManager extends ProfileContentManager {
} }
async acceptRequest(remoteProfileId: number) { async acceptRequest(remoteProfileId: number) {
const requests = await this.#getIncomingRequests(); const requests = await this.getIncomingRequests();
if (!requests.includes(remoteProfileId)) return; if (!requests.includes(remoteProfileId)) return;
await this.#clearAssociationWithRemote(remoteProfileId); await this.#clearAssociationWithRemote(remoteProfileId);
const remoteRootKey = this.createRemoteRootKey(remoteProfileId); const remoteRootKey = this.#createRemoteRootKey(remoteProfileId);
await Redis.Database.sadd(this.#friendsKey, remoteProfileId); await Redis.Database.sadd(this.#friendsKey, remoteProfileId);
await Redis.Database.sadd(Redis.buildKey(remoteRootKey, Redis.KeyGroups.Profiles.Relationships.Friends), this.profileId); await Redis.Database.sadd(Redis.buildKey(remoteRootKey, Redis.KeyGroups.Profiles.Relationships.Friends), this.profileId);
} }
async denyRequest(remoteProfileId: number) { async denyRequest(remoteProfileId: number) {
const requests = await this.#getIncomingRequests(); const requests = await this.getIncomingRequests();
if (!requests.includes(remoteProfileId)) return; if (!requests.includes(remoteProfileId)) return;
await this.#clearAssociationWithRemote(remoteProfileId); await this.#clearAssociationWithRemote(remoteProfileId);
} }
async getRelationshipType(remoteProfileId: number) { async getRelationshipType(remoteProfileId: number) {
const isFriendsWithRemote = (await Redis.Database.sismember(this.#friendsKey, remoteProfileId)) >= 1; const isFriendsWithRemote = (await Redis.Database.sismember(this.#friendsKey, remoteProfileId)) >= 1;
const remoteSentRequest = (await this.#getIncomingRequests()).includes(remoteProfileId); const remoteSentRequest = (await this.getIncomingRequests()).includes(remoteProfileId);
const localSentRequest = (await this.#getOutgoingRequests()).includes(remoteProfileId); const localSentRequest = (await this.getOutgoingRequests()).includes(remoteProfileId);
if (isFriendsWithRemote || (remoteSentRequest && localSentRequest)) return RelationshipType.Friend; if (isFriendsWithRemote || (remoteSentRequest && localSentRequest)) return RelationshipType.Friend;
else if (remoteSentRequest) return RelationshipType.FriendRequestReceived; else if (remoteSentRequest) return RelationshipType.FriendRequestReceived;
@@ -177,7 +178,7 @@ export class ProfileRelationshipManager extends ProfileContentManager {
} }
async getMutedReciprocal(remoteProfileId: number) { async getMutedReciprocal(remoteProfileId: number) {
const remoteKey = this.createRemoteRootKey(remoteProfileId); const remoteKey = this.#createRemoteRootKey(remoteProfileId);
const localMuted = (await this.getAllMuted()).includes(remoteProfileId); const localMuted = (await this.getAllMuted()).includes(remoteProfileId);
const remoteMuted = await Redis.Database.sismember(Redis.buildKey(remoteKey, Redis.KeyGroups.Profiles.Relationships.Muted), this.profileId); const remoteMuted = await Redis.Database.sismember(Redis.buildKey(remoteKey, Redis.KeyGroups.Profiles.Relationships.Muted), this.profileId);
@@ -189,7 +190,7 @@ export class ProfileRelationshipManager extends ProfileContentManager {
} }
async getIgnoredReciprocal(remoteProfileId: number) { async getIgnoredReciprocal(remoteProfileId: number) {
const remoteKey = this.createRemoteRootKey(remoteProfileId); const remoteKey = this.#createRemoteRootKey(remoteProfileId);
const localIgnored = (await this.getAllMuted()).includes(remoteProfileId); const localIgnored = (await this.getAllMuted()).includes(remoteProfileId);
const remoteIgnored = await Redis.Database.sismember(Redis.buildKey(remoteKey, Redis.KeyGroups.Profiles.Relationships.Ignoring), this.profileId); const remoteIgnored = await Redis.Database.sismember(Redis.buildKey(remoteKey, Redis.KeyGroups.Profiles.Relationships.Ignoring), this.profileId);
@@ -211,12 +212,12 @@ export class ProfileRelationshipManager extends ProfileContentManager {
} }
async sendPlayerFriendRequest(player: Profile) { async sendPlayerFriendRequest(player: Profile) {
await Redis.Database.sadd(Redis.buildKey(this.createRemoteRootKey(player.getId()), Redis.KeyGroups.Profiles.Relationships.IncomingFriendRequests), this.profileId); await Redis.Database.sadd(Redis.buildKey(this.#createRemoteRootKey(player.getId()), Redis.KeyGroups.Profiles.Relationships.IncomingFriendRequests), this.profileId);
await Redis.Database.sadd(this.#outgoingFriends, player.getId()); await Redis.Database.sadd(this.#outgoingFriends, player.getId());
} }
async removePlayerFriendRequest(player: Profile) { async removePlayerFriendRequest(player: Profile) {
await Redis.Database.srem(Redis.buildKey(this.createRemoteRootKey(player.getId()), Redis.KeyGroups.Profiles.Relationships.IncomingFriendRequests), this.profileId); await Redis.Database.srem(Redis.buildKey(this.#createRemoteRootKey(player.getId()), Redis.KeyGroups.Profiles.Relationships.IncomingFriendRequests), this.profileId);
await Redis.Database.srem(this.#outgoingFriends, player.getId()); await Redis.Database.srem(this.#outgoingFriends, player.getId());
} }

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -23,11 +23,15 @@ export class ProfileRoomsManager extends ProfileContentManager {
#rootKey = Redis.buildKey( #rootKey = Redis.buildKey(
Redis.KeyGroups.Profiles.Root, Redis.KeyGroups.Profiles.Root,
this.profileId.toString(), this.profileId.toString(),
Redis.KeyGroups.Profiles.Avatar.Root Redis.KeyGroups.Profiles.Rooms
); );
async getRooms() { async getOwnedRoomIds() {
// get player rooms return (await Redis.Database.smembers(this.#rootKey)).map(val => parseInt(val)).filter(val => !isNaN(val));
}
async addOwnedRoomId(id: number) {
await Redis.Database.sadd(this.#rootKey, id.toString());
} }
} }

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -16,23 +16,25 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { Redis } from "../db.ts"; import { Redis } from "../db.ts";
import Logging from "@proxnet/undead-logging";
import Dictionary from "./usernames.ts"; import Dictionary from "./usernames.ts";
import { Config } from "../config.ts"; import { Config } from "../config.ts";
import { AuthType } from "./users.ts"; import { AuthType } from "./users.ts";
import * as JsonWebToken from "@gz/jwt"; import * as JsonWebToken from "@gz/jwt";
import { TokenBaseFormat } from "../apiutils.ts"; import { TokenBaseFormat } from "../apiutils.ts";
import { DeviceClass, RoomInstance, VRMovementMode } from "./live/types.ts"; import { DeviceClass, VRMovementMode } from "./live/types.ts";
import { SettingKey } from "./content/settings.ts"; import { SettingKey } from "./content/settings.ts";
import { z } from "zod"; import { z } from "zod";
import { SignalRSocketHandler } from "../socket/socket.ts"; import { SignalRSocketHandler } from "../socket/socket.ts";
import { ProfileSettingsManager } from "./profile/settings.ts"; import { ProfileSettingsManager } from "./profile/settings.ts";
import { ProfileProgressionManager } from "./profile/progression.ts"; import { ProfileProgressionManager } from "./profile/progression.ts";
import { ProfileReputationManager } from "./profile/reputation.ts"; import { ProfileReputationManager } from "./profile/reputation.ts";
import Logging from "@proxnet/undead-logging";
import { ProfileRelationshipManager } from "./profile/relationships.ts"; import { ProfileRelationshipManager } from "./profile/relationships.ts";
import { ProfileAvatarManager } from "./profile/avatar.ts"; import { ProfileAvatarManager } from "./profile/avatar.ts";
import { EventManager } from "./baseevent.ts"; import { EventManager } from "./baseevent.ts";
import { ProfileEvents, ProfileUpdatedEvent } from "./profileevents.ts"; import { ProfileEvents, ProfileUpdatedEvent } from "./profileevents.ts";
import { Instance } from "./live/instances.ts";
import { ProfileRoomsManager } from "./profile/rooms.ts";
const config = Config.getConfig(); const config = Config.getConfig();
@@ -149,7 +151,6 @@ class Profile extends EventManager {
return profile; return profile;
} }
// surely this can be written better
static getExportAccount(id: number): Promise<SelfAccountExport | null> { static getExportAccount(id: number): Promise<SelfAccountExport | null> {
return new Promise((resolve, _reject) => { return new Promise((resolve, _reject) => {
Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Profiles.Root, id.toString(), Redis.KeyGroups.Profiles.Username)).then((val) => { Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Profiles.Root, id.toString(), Redis.KeyGroups.Profiles.Username)).then((val) => {
@@ -177,6 +178,11 @@ class Profile extends EventManager {
}); });
} }
/**
* Get player account exports in bulk. Returns only profiles that exist.
* @param ids Player IDs
* @returns Promise - Array of Profiles
*/
static async getExportAccountsBulk(ids: number[]) { static async getExportAccountsBulk(ids: number[]) {
const accs = await Promise.all( const accs = await Promise.all(
ids.map((val) => this.getExportAccount(val)), ids.map((val) => this.getExportAccount(val)),
@@ -186,7 +192,7 @@ class Profile extends EventManager {
#id: number; #id: number;
#instance: RoomInstance | null = null; #instance: Instance | null = null;
#socket: SignalRSocketHandler | null = null; #socket: SignalRSocketHandler | null = null;
@@ -195,6 +201,7 @@ class Profile extends EventManager {
Reputation: ProfileReputationManager; Reputation: ProfileReputationManager;
Relationships: ProfileRelationshipManager; Relationships: ProfileRelationshipManager;
Avatar: ProfileAvatarManager; Avatar: ProfileAvatarManager;
Rooms: ProfileRoomsManager;
constructor(id: number) { constructor(id: number) {
super(); super();
@@ -206,6 +213,7 @@ class Profile extends EventManager {
this.Reputation = new ProfileReputationManager(this.#id); this.Reputation = new ProfileReputationManager(this.#id);
this.Relationships = new ProfileRelationshipManager(this.#id); this.Relationships = new ProfileRelationshipManager(this.#id);
this.Avatar = new ProfileAvatarManager(this.#id); this.Avatar = new ProfileAvatarManager(this.#id);
this.Rooms = new ProfileRoomsManager(this.#id);
} }
#emitProfileUpdated() { #emitProfileUpdated() {
@@ -216,7 +224,7 @@ class Profile extends EventManager {
this.emit(ProfileEvents.BaseUpdated, ev); this.emit(ProfileEvents.BaseUpdated, ev);
} }
setInstance(instance: RoomInstance | null) { setInstance(instance: Instance | null) {
this.#instance = instance; this.#instance = instance;
} }

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -24,7 +24,6 @@ import { TokenBaseFormat } from "../apiutils.ts";
type UserInitOptions = { type UserInitOptions = {
client_id: string; client_id: string;
pubkey: string; pubkey: string;
captcha?: string;
}; };
export enum AuthType { export enum AuthType {

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -71,7 +71,7 @@ export const KeyGroups = {
}, },
Content: { Content: {
Root: "content", Root: "content",
Files: "file-meta", Words: "charades"
}, },
Profile_Usernames: "profile-usernames", Profile_Usernames: "profile-usernames",
PlatformAssociations: "platforms", PlatformAssociations: "platforms",

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -27,15 +27,16 @@ import { decode } from "@gz/jwt";
import UnifiedProfile, { ProfileTokenFormat } from "./data/profiles.ts"; import UnifiedProfile, { ProfileTokenFormat } from "./data/profiles.ts";
import { SocketHandoff } from "./socket/handoff.ts"; import { SocketHandoff } from "./socket/handoff.ts";
import { SignalRSocketHandler } from "./socket/socket.ts"; import { SignalRSocketHandler } from "./socket/socket.ts";
import Rooms from "./data/content/rooms.ts";
import { GameConfigs } from "./data/config.ts"; import { GameConfigs } from "./data/config.ts";
import { RootPath } from "./data/content/baseimages.ts"; import { getVersion } from "./ver.ts";
import Rooms from "./data/content/rooms.ts";
import { PushNotificationId } from "./socket/types.ts";
const instanceId = generateRandomString(64); const instanceId = generateRandomString(64);
const log = new Log.default("Main"); const log = new Log.default("Main");
log.i(`Starting Galvanic Corrosion..`); log.i(`Galvanic Corrosion '${await getVersion()}'`);
const config = Config.getConfig(); const config = Config.getConfig();
@@ -158,7 +159,7 @@ try {
}}, async (req: Request, info: Deno.ServeHandlerInfo<Deno.NetAddr>) => { }}, async (req: Request, info: Deno.ServeHandlerInfo<Deno.NetAddr>) => {
const path = new URL(req.url).pathname; const path = new URL(req.url).pathname;
const upgrade = req.headers.get('Upgrade') === 'websocket'; const upgrade = req.headers.get('Upgrade') === 'websocket';
log.n(`U:${upgrade}; ${info.remoteAddr.hostname}:${info.remoteAddr.port} ${req.method} ${path}`); log.n(`SOCK U:${upgrade}; ${info.remoteAddr.hostname}:${info.remoteAddr.port} ${req.method} ${path}`);
if (path === '/negotiate' && req.method == 'POST') if (path === '/negotiate' && req.method == 'POST')
return new Response(JSON.stringify({})); return new Response(JSON.stringify({}));
@@ -199,15 +200,17 @@ try {
Deno.addSignalListener("SIGINT", () => { Deno.addSignalListener("SIGINT", () => {
if (shuttingDown) return; if (shuttingDown) return;
shuttingDown = true; shuttingDown = true;
for (const handoff of SocketHandoff.all()) handoff.complete();
for (const sock of UnifiedProfile.getAllSockets()) sock.sendNotification(PushNotificationId.ModerationQuitGame);
});
Deno.addSignalListener("SIGINT", () => {
if (shuttingDown) return;
log.i(`Shutting down`); log.i(`Shutting down`);
abort.abort(); // websockets abort.abort(); // websockets
http.close(); http.close();
http.closeAllConnections(); http.closeAllConnections();
}); });
Deno.addSignalListener("SIGINT", () => {
for (const handoff of SocketHandoff.all()) handoff.complete();
});
if (!(await UnifiedProfile.existsByName("Coach"))) UnifiedProfile.create({ username: "Coach", id: 1 }); // create Coach id 1 if they do not exist if (!(await UnifiedProfile.existsByName("Coach"))) UnifiedProfile.create({ username: "Coach", id: 1 }); // create Coach id 1 if they do not exist
if (!(await UnifiedProfile.existsByName("Server"))) UnifiedProfile.create({ username: "Server", id: 2 }); // create Server id 2 if they do not exist if (!(await UnifiedProfile.existsByName("Server"))) UnifiedProfile.create({ username: "Server", id: 2 }); // create Server id 2 if they do not exist

20
src/path.ts Normal file
View File

@@ -0,0 +1,20 @@
/* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { platform } from "node:process";
export const RootPath = Deno.mainModule.substring(platform == 'win32' ? 8 : 7, Deno.mainModule.length - 11);

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -15,14 +15,17 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { APIUtils } from "../../apiutils.ts"; import { APIUtils, NoBody } from "../../apiutils.ts";
import express from "express"; import express from "express";
import UnifiedProfile, { Profile } from "../../data/profiles.ts"; import UnifiedProfile, { Profile } from "../../data/profiles.ts";
import { z } from "zod"; import { z } from "zod";
import { AuthType } from "../../data/users.ts"; import { AuthType } from "../../data/users.ts";
import Logging from "@proxnet/undead-logging";
export const route = APIUtils.createRouter("/account"); export const route = APIUtils.createRouter("/account");
const log = new Logging("AccountRoute");
const CreateAccountRequestBodySchema = z.object({ const CreateAccountRequestBodySchema = z.object({
platform: z.string(), platform: z.string(),
platformId: z.string(), platformId: z.string(),
@@ -70,11 +73,9 @@ route.router.get("/bulk",
} else if (typeof rq.query.id == "string") { } else if (typeof rq.query.id == "string") {
const id = parseInt(rq.query.id, 10); const id = parseInt(rq.query.id);
if (isNaN(id)) { if (isNaN(id)) {
rs.json( rs.json(APIUtils.genericResponseFormat(true, "Query data error"));
APIUtils.genericResponseFormat(true, "Query data error"),
);
return; return;
} else { } else {
rs.json([await Profile.getExportAccount(id)].filter((val) => val !== null)); rs.json([await Profile.getExportAccount(id)].filter((val) => val !== null));
@@ -116,7 +117,7 @@ route.router.put("/me/displayname",
express.urlencoded({ extended: true }), express.urlencoded({ extended: true }),
APIUtils.validateRequestBody(DisplayNameUpdateSchema), APIUtils.validateRequestBody(DisplayNameUpdateSchema),
(rq: express.Request<{}, {}, DisplayNameUpdate>, rs: express.Response, nxt: express.NextFunction) => { (rq: express.Request<NoBody, NoBody, DisplayNameUpdate>, rs: express.Response, nxt: express.NextFunction) => {
rs.locals.profile.setDisplayName(rq.body.displayName); rs.locals.profile.setDisplayName(rq.body.displayName);
nxt(); nxt();
}, },

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -39,6 +39,7 @@ import { route as CommunityBoardRoute } from "./api/communityboard.ts";
import { route as PlayerEventsRoute } from "./api/playerevents.ts"; import { route as PlayerEventsRoute } from "./api/playerevents.ts";
import { route as StorefrontsRoute } from "./api/storefronts.ts"; import { route as StorefrontsRoute } from "./api/storefronts.ts";
import { route as AnnouncementRoute } from "./api/announcement.ts"; import { route as AnnouncementRoute } from "./api/announcement.ts";
import { route as ActivitiesRoute } from "./api/activities.ts";
export const route = APIUtils.createRouter("/api"); export const route = APIUtils.createRouter("/api");
@@ -64,4 +65,5 @@ route.router.use(ImagesRoute.path, ImagesRoute.router);
route.router.use(CommunityBoardRoute.path, CommunityBoardRoute.router); route.router.use(CommunityBoardRoute.path, CommunityBoardRoute.router);
route.router.use(PlayerEventsRoute.path, PlayerEventsRoute.router); route.router.use(PlayerEventsRoute.path, PlayerEventsRoute.router);
route.router.use(StorefrontsRoute.path, StorefrontsRoute.router); route.router.use(StorefrontsRoute.path, StorefrontsRoute.router);
route.router.use(AnnouncementRoute.path, AnnouncementRoute.router); route.router.use(AnnouncementRoute.path, AnnouncementRoute.router);
route.router.use(ActivitiesRoute.path, ActivitiesRoute.router);

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -0,0 +1,34 @@
/* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { APIUtils } from "../../apiutils.ts";
import { getWords } from "../../data/content/activities.ts";
export const route = APIUtils.createRouter("/activities");
const rateLimit = new APIUtils.RateLimiter();
const words = getWords();
route.router.get('/charades/v1/words',
rateLimit.middle(),
(_rq, rs) => {
rs.json(words);
},
);

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -16,6 +16,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { APIUtils } from "../../apiutils.ts"; import { APIUtils } from "../../apiutils.ts";
import { ObjectiveType } from "../../data/objectives.ts";
import { AuthType } from "../../data/users.ts"; import { AuthType } from "../../data/users.ts";
export const route = APIUtils.createRouter('/checklist'); export const route = APIUtils.createRouter('/checklist');
@@ -25,6 +26,171 @@ route.router.get('/v1/current',
APIUtils.Authentication, APIUtils.Authentication,
APIUtils.AuthenticationType(AuthType.Game), APIUtils.AuthenticationType(AuthType.Game),
APIUtils.emptyArrayResponse (_rq, rs) => {
rs.json([
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
},
{
Order: 1,
Objective: ObjectiveType.CompleteAnyDaily,
Count: 1,
CreditAmount: -1
}
]);
},
); );

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -36,6 +36,17 @@ route.router.get('/v1/myprogress',
); );
route.router.post('/v1/updateobjective',
APIUtils.Authentication,
APIUtils.AuthenticationType(AuthType.Game),
(_rq, rs) => {
rs.sendStatus(200);
},
)
interface ClearGroupRequestBody { interface ClearGroupRequestBody {
Group: number Group: number
} }

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -57,7 +57,7 @@ route.router.post('/v1/bulk',
express.urlencoded({ extended: true }), express.urlencoded({ extended: true }),
APIUtils.validateRequestBody(reputationBulkSchema), APIUtils.validateRequestBody(reputationBulkSchema),
async (rq: express.Request<NoBody, {}, ReputationBulkBody>, rs: express.Response) => { async (rq: express.Request<NoBody, NoBody, ReputationBulkBody>, rs: express.Response) => {
if (typeof rq.body.Ids == 'object') { if (typeof rq.body.Ids == 'object') {
const reputations = rq.body.Ids const reputations = rq.body.Ids
.map(id => parseInt(id)).filter(id => !isNaN(id)) // parse as int[] and filter out non-numbers .map(id => parseInt(id)).filter(id => !isNaN(id)) // parse as int[] and filter out non-numbers

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -68,7 +68,7 @@ route.router.post('/v1/progression/bulk',
express.urlencoded({ extended: true }), express.urlencoded({ extended: true }),
APIUtils.validateRequestBody(progressionBulkSchema), APIUtils.validateRequestBody(progressionBulkSchema),
async (rq: express.Request<NoBody, {}, ProgressionBulkBody>, rs: express.Response) => { async (rq: express.Request<NoBody, NoBody, ProgressionBulkBody>, rs: express.Response) => {
if (typeof rq.body.Ids == 'object') { if (typeof rq.body.Ids == 'object') {
const progressions = rq.body.Ids const progressions = rq.body.Ids
.map(id => parseInt(id)).filter(id => !isNaN(id)) // filter out non-numbers .map(id => parseInt(id)).filter(id => !isNaN(id)) // filter out non-numbers

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -15,11 +15,13 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { APIUtils } from "../../apiutils.ts"; import { z } from "zod";
import { APIUtils, NoBody } from "../../apiutils.ts";
import Rooms from "../../data/content/rooms.ts"; import Rooms from "../../data/content/rooms.ts";
import { RoomDataTypes } from "../../data/content/rooms/DataTypes.ts"; import { RoomDataTypes } from "../../data/content/rooms/DataTypes.ts";
import { AuthType } from "../../data/users.ts"; import { AuthType } from "../../data/users.ts";
import express from "express"; import express from "express";
import { RoomFactory } from "../../data/content/rooms/RoomFactory.ts";
export const route = APIUtils.createRouter("/rooms"); export const route = APIUtils.createRouter("/rooms");
@@ -54,7 +56,19 @@ route.router.get('/v2/myrooms',
APIUtils.Authentication, APIUtils.Authentication,
APIUtils.AuthenticationType(AuthType.Game), APIUtils.AuthenticationType(AuthType.Game),
APIUtils.emptyArrayResponse async (_rq, rs) => {
const ids = await rs.locals.profile.Rooms.getOwnedRoomIds();
if (ids.length == 0) {
rs.json([]);
return;
}
const roomFactoriesPreInit = ids.map(id => new RoomFactory({ id: id }));
const roomFactories = (await Promise.all(roomFactoriesPreInit.map(factory => factory.init()))).filter(val => val !== null);
const detailsPromises = (await Promise.all(roomFactories.map(factory => factory.export())));
rs.json(detailsPromises.map(roomDetails => roomDetails.Room));
},
); );
@@ -119,8 +133,58 @@ route.router.post('/v1/roomRolePermissions',
APIUtils.Authentication, APIUtils.Authentication,
APIUtils.AuthenticationType(AuthType.Game), APIUtils.AuthenticationType(AuthType.Game),
(rq, rs) => { (_rq, rs) => {
rs.sendStatus(200); rs.sendStatus(200);
}, },
); );
route.router.get('/v1/agRoomIds',
APIUtils.Authentication,
APIUtils.AuthenticationType(AuthType.Game),
async (_rq, rs) => {
const rooms = await Rooms.getAllBuiltinRoomGenerations();
rs.json(rooms.map(det => det.Room.RoomId));
},
);
const CloneRoomSchema = z.object({
Name: z.string(),
RoomId: z.number()
});
interface CloneRoomBody {
Name: string,
RoomId: number
}
route.router.post('/v1/clone',
APIUtils.Authentication,
APIUtils.AuthenticationType(AuthType.Game),
express.json(),
APIUtils.validateRequestBody(CloneRoomSchema),
async (rq: express.Request<NoBody, NoBody, CloneRoomBody>, rs: express.Response) => {
const room = await Rooms.cloneRoom(rq.body.RoomId, rq.body.Name, rs.locals.profile);
const masterRoomFactory = await new RoomFactory({ id: rq.body.RoomId }).init();
rs.json({
Result: room.result,
RoomDetails: room.result == RoomDataTypes.CreateModifyRoomStatus.Success ? await room.factory?.export() : await masterRoomFactory?.export()
});
if (
room.result == RoomDataTypes.CreateModifyRoomStatus.Success
&& room.factory
) rs.locals.profile.Rooms.addOwnedRoomId(room.factory.RoomId);
},
);

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -18,7 +18,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { APIUtils } from "../../apiutils.ts"; import { APIUtils } from "../../apiutils.ts";
import express from "express"; import express from "express";
import { AuthType } from "../../data/users.ts"; import { AuthType } from "../../data/users.ts";
import { CurrencyType, StorefrontBalanceType } from "../../data/content/storefronts.ts"; import { StorefrontBalanceType } from "../../data/content/storefronts.ts";
export const route = APIUtils.createRouter('/storefronts'); export const route = APIUtils.createRouter('/storefronts');

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -16,11 +16,12 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ along with this program. If not, see <https://www.gnu.org/licenses/>. */
import Logging from "@proxnet/undead-logging"; import Logging from "@proxnet/undead-logging";
import { APIUtils } from "../../apiutils.ts"; import { APIUtils, NoBody } from "../../apiutils.ts";
import Matchmaking from "../../data/live/base.ts"; import Matchmaking from "../../data/live/base.ts";
import { MatchmakingErrorCode } from "../../data/live/types.ts"; import { MatchmakingErrorCode } from "../../data/live/types.ts";
import { AuthType } from "../../data/users.ts"; import { AuthType } from "../../data/users.ts";
import express from "express"; import express from "express";
import { z } from "zod";
const log = new Logging("MatchGotoRoute"); const log = new Logging("MatchGotoRoute");
@@ -31,13 +32,31 @@ interface MatchmakingParams {
subRoomName?: string subRoomName?: string
} }
const ProperCaseBooleanSchema = z.preprocess((val) => {
if (val === "True") return true;
if (val === "False") return false;
if (typeof val === "boolean") return val; // allow raw booleans too
return val; // will fail validation
}, z.boolean());
interface MatchmakingOptions {
CreatePrivateInstance?: string,
BypassMovementModeRestriction?: string
}
route.router.post('/room/:roomName', route.router.post('/room/:roomName',
APIUtils.Authentication, APIUtils.Authentication,
APIUtils.AuthenticationType(AuthType.Game), APIUtils.AuthenticationType(AuthType.Game),
APIUtils.startTimer, APIUtils.startTimer,
express.urlencoded({ extended: true }),
APIUtils.validateRequestBody(z.object({
CreatePrivateInstance: ProperCaseBooleanSchema.optional(),
BypassMovementModeRestriction: ProperCaseBooleanSchema.optional()
})),
async (rq: express.Request<MatchmakingParams>, rs: express.Response, nxt: express.NextFunction) => { async (rq: express.Request<MatchmakingParams, NoBody, MatchmakingOptions>, rs: express.Response, nxt: express.NextFunction) => {
log.d(`Player ${rs.locals.profile.getId()} is requesting to matchmake\n Room: '${rq.params.roomName}'\n Body: ${JSON.stringify(rq.body)}`);
if (!rq.params.roomName) { if (!rq.params.roomName) {
log.d('Matchmake failed: No room specified'); log.d('Matchmake failed: No room specified');
rs.json({ rs.json({
@@ -45,7 +64,11 @@ route.router.post('/room/:roomName',
}); });
return; return;
} }
rs.json(await Matchmaking.matchmake({ profile: rs.locals.profile, roomName: rq.params.roomName })); rs.json(await Matchmaking.matchmake({
profile: rs.locals.profile,
roomName: rq.params.roomName,
private: rq.body.CreatePrivateInstance ? rq.body.CreatePrivateInstance == 'True' : undefined
}));
nxt(); nxt();
}, },
@@ -57,8 +80,10 @@ route.router.post('/room/:roomName/:subRoomName',
APIUtils.Authentication, APIUtils.Authentication,
APIUtils.AuthenticationType(AuthType.Game), APIUtils.AuthenticationType(AuthType.Game),
APIUtils.startTimer, APIUtils.startTimer,
express.urlencoded({ extended: true }),
async (rq: express.Request<MatchmakingParams>, rs: express.Response, nxt: express.NextFunction) => { async (rq: express.Request<MatchmakingParams, NoBody, MatchmakingOptions>, rs: express.Response, nxt: express.NextFunction) => {
log.d(`Player ${rs.locals.profile.getId()} is requesting to matchmake\n Room: '${rq.params.roomName}'\n Subroom: '${rq.params.subRoomName}'\n Body: ${JSON.stringify(rq.body)}`);
if (!rq.params.roomName) { if (!rq.params.roomName) {
log.d('Matchmake failed: No room specified'); log.d('Matchmake failed: No room specified');
rs.json({ rs.json({
@@ -66,7 +91,12 @@ route.router.post('/room/:roomName/:subRoomName',
}); });
return; return;
} }
rs.json(await Matchmaking.matchmake({ profile: rs.locals.profile, roomName: rq.params.roomName, subRoomName: rq.params.subRoomName })); rs.json(await Matchmaking.matchmake({
profile: rs.locals.profile,
roomName: rq.params.roomName,
subRoomName: rq.params.subRoomName,
private: rq.body.CreatePrivateInstance ? rq.body.CreatePrivateInstance == 'True' : undefined
}));
nxt(); nxt();
}, },

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -25,7 +25,6 @@ import Logging from "@proxnet/undead-logging";
import UnifiedProfile from "../../data/profiles.ts"; import UnifiedProfile from "../../data/profiles.ts";
import { PlayerStatusVisibility, VRMovementMode } from "../../data/live/types.ts"; import { PlayerStatusVisibility, VRMovementMode } from "../../data/live/types.ts";
import { SettingKey } from "../../data/content/settings.ts"; import { SettingKey } from "../../data/content/settings.ts";
import Instances from "../../data/live/instances.ts";
const log = new Logging("MatchPlayerRoute"); const log = new Logging("MatchPlayerRoute");
@@ -86,7 +85,7 @@ route.router.post('/logout',
APIUtils.validateRequestBody(LoginSchema), APIUtils.validateRequestBody(LoginSchema),
(_rq, rs) => { (_rq, rs) => {
Instances.removePlayerFromCurrentInstance(rs.locals.profile); rs.locals.profile.getInstance()?.removePlayer(rs.locals.profile);
rs.sendStatus(200); rs.sendStatus(200);
} }
@@ -129,10 +128,10 @@ route.router.put('/statusvisibility',
); );
interface VRMovementModeBody { interface VRMovementModeBody {
vrMovementMode: PlayerStatusVisibility vrMovementMode: VRMovementMode
} }
const VRMovementModeSchema = z.object({ const VRMovementModeSchema = z.object({
vrMovementMode: z.nativeEnum(VRMovementMode) vrMovementMode: z.enum(Object.values(VRMovementMode).map(String) as [string, ...string[]])
}); });
route.router.put('/vrmovementmode', route.router.put('/vrmovementmode',
@@ -155,7 +154,7 @@ route.router.put('/photonregionpings',
APIUtils.Authentication, APIUtils.Authentication,
APIUtils.AuthenticationType(AuthType.Game), APIUtils.AuthenticationType(AuthType.Game),
(rq, rs) => { (_rq, rs) => {
rs.sendStatus(200); rs.sendStatus(200);
} }

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -48,8 +48,8 @@ route.router.get('/:id/instances',
roomId: parsedId, roomId: parsedId,
subRoomId: instance.subRoomId, subRoomId: instance.subRoomId,
isFull: instance.isFull, isFull: instance.isFull,
createdAt: new Date().toISOString(), // TODO: rewrite instance - create instance class rather than using sets - include datetime when instance was created createdAt: instance.timeCreated,
playerIds: Instances.getInstancePlayers(instance).values().toArray().map(profile => profile.getId()) playerIds: instance.getAllPlayers().map(profile => profile.getId())
}))); })));
}, },

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
@@ -34,10 +34,9 @@ import {
import { SocketTarget } from "./targets/targetbase.ts"; import { SocketTarget } from "./targets/targetbase.ts";
import { PlayerSocketSubscriptionTarget } from "./targets/SubscribeToPlayers.ts"; import { PlayerSocketSubscriptionTarget } from "./targets/SubscribeToPlayers.ts";
import Presence from "../data/live/presence.ts"; import Presence from "../data/live/presence.ts";
import Instances from "../data/live/instances.ts";
import Matchmaking from "../data/live/base.ts"; import Matchmaking from "../data/live/base.ts";
const logmessages = true; const logmessages = false;
export class SignalRSocketHandler { export class SignalRSocketHandler {
@@ -50,6 +49,8 @@ export class SignalRSocketHandler {
#PeriodicalId: number; #PeriodicalId: number;
#killed = false;
constructor(socket: WebSocket, player: Profile) { constructor(socket: WebSocket, player: Profile) {
this.#socket = socket; this.#socket = socket;
@@ -62,6 +63,7 @@ export class SignalRSocketHandler {
this.#Targets.set('SubscribeToPlayers', new PlayerSocketSubscriptionTarget(this)); this.#Targets.set('SubscribeToPlayers', new PlayerSocketSubscriptionTarget(this));
this.#PeriodicalId = setInterval(async () => { this.#PeriodicalId = setInterval(async () => {
if (this.#killed) return;
if (this.#socket.readyState !== this.#socket.CLOSED) { if (this.#socket.readyState !== this.#socket.CLOSED) {
const pres = await Presence.get(this.#profile); const pres = await Presence.get(this.#profile);
this.sendNotification("PresenceUpdate", await pres.export()); this.sendNotification("PresenceUpdate", await pres.export());
@@ -69,9 +71,18 @@ export class SignalRSocketHandler {
} }
}, 8000); }, 8000);
this.#socket.onclose = (ev) => {
this.#log.d(`Close reason: ${ev.reason}`);
}
} }
async #dispatchTarget<T = unknown>(target: string, args: unknown): Promise<TargetResult> { async #dispatchTarget<T = unknown>(target: string, args: unknown): Promise<TargetResult> {
if (this.#killed) {
const error = "Tried to dispatch socket target on dead socket";
this.#log.w(error);
return { type: TargetResultType.Failure, err: error };
}
const targetExec = this.#Targets.get(target); const targetExec = this.#Targets.get(target);
if (!targetExec) return { type: TargetResultType.NotATarget } as TargetResultNotATarget; if (!targetExec) return { type: TargetResultType.NotATarget } as TargetResultNotATarget;
else { else {
@@ -152,6 +163,7 @@ export class SignalRSocketHandler {
destroy(sock: SignalRSocketHandler, internal: boolean | undefined = false) { destroy(sock: SignalRSocketHandler, internal: boolean | undefined = false) {
return () => { return () => {
sock.#killed = true;
clearInterval(sock.#PeriodicalId); clearInterval(sock.#PeriodicalId);
sock.sendRaw({ type: 7, error: "Socket closed" }); sock.sendRaw({ type: 7, error: "Socket closed" });
if (!internal) sock.#socket.close(); if (!internal) sock.#socket.close();
@@ -160,7 +172,7 @@ export class SignalRSocketHandler {
for (const target of sock.#Targets.values()) target.destroy(); for (const target of sock.#Targets.values()) target.destroy();
Instances.removePlayerFromCurrentInstance(this.#profile); this.#profile.getInstance()?.removePlayer(this.#profile);
Matchmaking.deleteLoginLock(this.#profile); Matchmaking.deleteLoginLock(this.#profile);
} }
} }
@@ -174,13 +186,13 @@ export class SignalRSocketHandler {
} }
} }
sendNotification(id: PushNotificationId | string, args: object) { sendNotification(id: PushNotificationId | string, args?: object) {
const msg: SignalRMessage = { const msg: SignalRMessage = {
type: SignalMessageType.Invocation, type: SignalMessageType.Invocation,
target: "Notification", target: "Notification",
arguments: [JSON.stringify({ arguments: [JSON.stringify({
Id: id, Id: id,
Msg: args Msg: args ? args : {}
})] })]
} }
this.sendRaw(msg); this.sendRaw(msg);

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View File

@@ -1,6 +1,6 @@
/* Galvanic Corrosion - Rec Room custom server for communities. /* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion> <https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

25
src/ver.ts Normal file
View File

@@ -0,0 +1,25 @@
/* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
export async function getVersion(): Promise<'development' | 'unknown ver' | string> {
try {
const ver = await import('../ver.ts');
return ver.Version;
} catch {
return "unknown ver"
}
}

18
ver.ts Normal file
View File

@@ -0,0 +1,18 @@
/* Galvanic Corrosion - Rec Room custom server for communities.
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
export const Version = 'development';