mirror of
https://github.com/rommapp/romm.git
synced 2025-12-22 10:27:13 +00:00
add config entry to enable netplay
This commit is contained in:
@@ -84,6 +84,7 @@ class Config:
|
||||
HIGH_PRIO_STRUCTURE_PATH: str
|
||||
EJS_DEBUG: bool
|
||||
EJS_CACHE_LIMIT: int | None
|
||||
EJS_NETPLAY_ENABLED: bool
|
||||
EJS_NETPLAY_ICE_SERVERS: list[NetplayICEServer]
|
||||
EJS_SETTINGS: dict[str, EjsOption] # core_name -> EjsOption
|
||||
EJS_CONTROLS: dict[str, EjsControls] # core_name -> EjsControls
|
||||
@@ -227,6 +228,9 @@ class ConfigManager:
|
||||
EJS_CACHE_LIMIT=pydash.get(
|
||||
self._raw_config, "emulatorjs.cache_limit", None
|
||||
),
|
||||
EJS_NETPLAY_ENABLED=pydash.get(
|
||||
self._raw_config, "emulatorjs.netplay.enabled", None
|
||||
),
|
||||
EJS_NETPLAY_ICE_SERVERS=pydash.get(
|
||||
self._raw_config, "emulatorjs.netplay.ice_servers", []
|
||||
),
|
||||
@@ -401,6 +405,11 @@ class ConfigManager:
|
||||
log.critical("Invalid config.yml: emulatorjs.debug must be a boolean")
|
||||
sys.exit(3)
|
||||
|
||||
if not isinstance(self.config.EJS_NETPLAY_ENABLED, bool):
|
||||
log.critical(
|
||||
"Invalid config.yml: emulatorjs.netplay.enabled must be a boolean"
|
||||
)
|
||||
|
||||
if self.config.EJS_CACHE_LIMIT is not None and not isinstance(
|
||||
self.config.EJS_CACHE_LIMIT, int
|
||||
):
|
||||
@@ -524,6 +533,7 @@ class ConfigManager:
|
||||
"debug": self.config.EJS_DEBUG,
|
||||
"cache_limit": self.config.EJS_CACHE_LIMIT,
|
||||
"netplay": {
|
||||
"enabled": self.config.EJS_NETPLAY_ENABLED,
|
||||
"ice_servers": self.config.EJS_NETPLAY_ICE_SERVERS,
|
||||
},
|
||||
"settings": self.config.EJS_SETTINGS,
|
||||
|
||||
@@ -37,6 +37,7 @@ def get_config() -> ConfigResponse:
|
||||
SKIP_HASH_CALCULATION=cfg.SKIP_HASH_CALCULATION,
|
||||
EJS_DEBUG=cfg.EJS_DEBUG,
|
||||
EJS_CACHE_LIMIT=cfg.EJS_CACHE_LIMIT,
|
||||
EJS_NETPLAY_ENABLED=cfg.EJS_NETPLAY_ENABLED,
|
||||
EJS_NETPLAY_ICE_SERVERS=cfg.EJS_NETPLAY_ICE_SERVERS,
|
||||
EJS_CONTROLS=cfg.EJS_CONTROLS,
|
||||
EJS_SETTINGS=cfg.EJS_SETTINGS,
|
||||
|
||||
@@ -17,6 +17,7 @@ class ConfigResponse(TypedDict):
|
||||
SKIP_HASH_CALCULATION: bool
|
||||
EJS_DEBUG: bool
|
||||
EJS_CACHE_LIMIT: int | None
|
||||
EJS_NETPLAY_ENABLED: bool
|
||||
EJS_NETPLAY_ICE_SERVERS: list[NetplayICEServer]
|
||||
EJS_SETTINGS: dict[str, dict[str, str]]
|
||||
EJS_CONTROLS: dict[str, EjsControls]
|
||||
|
||||
@@ -56,6 +56,7 @@ emulatorjs:
|
||||
debug: true
|
||||
cache_limit: 1000
|
||||
netplay:
|
||||
enabled: true
|
||||
ice_servers:
|
||||
- urls: "stun:stun.relay.metered.ca:80"
|
||||
- urls: "turn:global.relay.metered.ca:80"
|
||||
|
||||
@@ -22,6 +22,7 @@ def test_config_loader():
|
||||
assert loader.config.SKIP_HASH_CALCULATION
|
||||
assert loader.config.EJS_DEBUG
|
||||
assert loader.config.EJS_CACHE_LIMIT == 1000
|
||||
assert loader.config.EJS_NETPLAY_ENABLED
|
||||
assert loader.config.EJS_NETPLAY_ICE_SERVERS == [
|
||||
{"urls": "stun:stun.relay.metered.ca:80"},
|
||||
{
|
||||
@@ -68,6 +69,7 @@ def test_empty_config_loader():
|
||||
assert not loader.config.SKIP_HASH_CALCULATION
|
||||
assert not loader.config.EJS_DEBUG
|
||||
assert loader.config.EJS_CACHE_LIMIT is None
|
||||
assert not loader.config.EJS_NETPLAY_ENABLED
|
||||
assert loader.config.EJS_NETPLAY_ICE_SERVERS == []
|
||||
assert loader.config.EJS_SETTINGS == {}
|
||||
assert loader.config.EJS_CONTROLS == {}
|
||||
|
||||
@@ -104,6 +104,14 @@ RUN apk add --no-cache \
|
||||
wget \
|
||||
ca-certificates
|
||||
|
||||
ARG EMULATORJS_VERSION=4.2.3
|
||||
ARG EMULATORJS_SHA256=07d451bc06fa3ad04ab30d9b94eb63ac34ad0babee52d60357b002bde8f3850b
|
||||
|
||||
RUN wget "https://github.com/EmulatorJS/EmulatorJS/releases/download/v${EMULATORJS_VERSION}/${EMULATORJS_VERSION}.7z" && \
|
||||
echo "${EMULATORJS_SHA256} ${EMULATORJS_VERSION}.7z" | sha256sum -c - && \
|
||||
7z x -y "${EMULATORJS_VERSION}.7z" -o/emulatorjs && \
|
||||
rm -f "${EMULATORJS_VERSION}.7z"
|
||||
|
||||
ARG RUFFLE_VERSION=nightly-2025-08-14
|
||||
ARG RUFFLE_FILE=ruffle-nightly-2025_08_14-web-selfhosted.zip
|
||||
ARG RUFFLE_SHA256=178870c5e7dd825a8df35920dfc5328d83e53f3c4d5d95f70b1ea9cd13494151
|
||||
@@ -225,6 +233,7 @@ CMD ["/init"]
|
||||
# FULL IMAGE
|
||||
FROM slim-image AS full-image
|
||||
ARG WEBSERVER_FOLDER=/var/www/html
|
||||
COPY --from=emulator-stage /emulatorjs ${WEBSERVER_FOLDER}/assets/emulatorjs
|
||||
COPY --from=emulator-stage /ruffle ${WEBSERVER_FOLDER}/assets/ruffle
|
||||
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ filesystem: {} # { roms_folder: 'roms' } For example if your folder structure is
|
||||
# default: # These settings apply to all cores
|
||||
# fps: show
|
||||
# netplay:
|
||||
# enabled: true
|
||||
# ice_servers:
|
||||
# - urls: "stun:stun.relay.metered.ca:80"
|
||||
# - urls: "turn:global.relay.metered.ca:80"
|
||||
|
||||
@@ -18,6 +18,7 @@ export type ConfigResponse = {
|
||||
SKIP_HASH_CALCULATION: boolean;
|
||||
EJS_DEBUG: boolean;
|
||||
EJS_CACHE_LIMIT: (number | null);
|
||||
EJS_NETPLAY_ENABLED: boolean;
|
||||
EJS_NETPLAY_ICE_SERVERS: Array<NetplayICEServer>;
|
||||
EJS_SETTINGS: Record<string, Record<string, string>>;
|
||||
EJS_CONTROLS: Record<string, EjsControls>;
|
||||
|
||||
@@ -84,8 +84,8 @@ export type DetailedRomSchema = {
|
||||
missing_from_fs: boolean;
|
||||
siblings: Array<SiblingRomSchema>;
|
||||
rom_user: RomUserSchema;
|
||||
merged_ra_metadata: (RomRAMetadata | null);
|
||||
merged_screenshots: Array<string>;
|
||||
merged_ra_metadata: (RomRAMetadata | null);
|
||||
user_saves: Array<SaveSchema>;
|
||||
user_states: Array<StateSchema>;
|
||||
user_screenshots: Array<ScreenshotSchema>;
|
||||
|
||||
@@ -79,7 +79,7 @@ export type SimpleRomSchema = {
|
||||
missing_from_fs: boolean;
|
||||
siblings: Array<SiblingRomSchema>;
|
||||
rom_user: RomUserSchema;
|
||||
merged_ra_metadata: (RomRAMetadata | null);
|
||||
merged_screenshots: Array<string>;
|
||||
merged_ra_metadata: (RomRAMetadata | null);
|
||||
};
|
||||
|
||||
|
||||
@@ -607,9 +607,11 @@ async function boot() {
|
||||
// Allow route transition animation to settle
|
||||
await new Promise((r) => setTimeout(r, 50));
|
||||
|
||||
const EMULATORJS_VERSION = "4.2.3";
|
||||
const LOCAL_PATH = "/assets/emulatorjs/data/";
|
||||
const CDN_PATH = `https://cdn.emulatorjs.org/${EMULATORJS_VERSION}/data/`;
|
||||
const EMULATORJS_VERSION = configStore.config.EJS_NETPLAY_ENABLED
|
||||
? "nightly"
|
||||
: "4.2.3";
|
||||
const LOCAL_PATH = "/assets/emulatorjs/data";
|
||||
const CDN_PATH = `https://cdn.emulatorjs.org/${EMULATORJS_VERSION}/data`;
|
||||
|
||||
function loadScript(src: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -625,15 +627,19 @@ async function boot() {
|
||||
async function attemptLoad(path: string, label: "local" | "cdn") {
|
||||
loaderStatus.value = label === "local" ? "loading-local" : "loading-cdn";
|
||||
window.EJS_pathtodata = path;
|
||||
await loadScript(`${path}loader.js`);
|
||||
await loadScript(`${path}/loader.js`);
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
await attemptLoad(LOCAL_PATH, "local");
|
||||
(await configStore.config.EJS_NETPLAY_ENABLED)
|
||||
? attemptLoad(CDN_PATH, "cdn")
|
||||
: attemptLoad(LOCAL_PATH, "local");
|
||||
} catch (e) {
|
||||
console.warn("[Play] Local loader failed, trying CDN", e);
|
||||
await attemptLoad(CDN_PATH, "cdn");
|
||||
(await configStore.config.EJS_NETPLAY_ENABLED)
|
||||
? attemptLoad(LOCAL_PATH, "local")
|
||||
: attemptLoad(CDN_PATH, "cdn");
|
||||
}
|
||||
// Wait for emulator bootstrap
|
||||
const startDeadline = Date.now() + 8000; // 8s
|
||||
|
||||
@@ -24,6 +24,7 @@ const defaultConfig = {
|
||||
PLATFORMS_VERSIONS: {},
|
||||
SKIP_HASH_CALCULATION: false,
|
||||
EJS_DEBUG: false,
|
||||
EJS_NETPLAY_ENABLED: false,
|
||||
EJS_CACHE_LIMIT: null,
|
||||
EJS_NETPLAY_ICE_SERVERS: [],
|
||||
EJS_SETTINGS: {},
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ROUTES } from "@/plugins/router";
|
||||
import firmwareApi from "@/services/api/firmware";
|
||||
import romApi from "@/services/api/rom";
|
||||
import storeAuth from "@/stores/auth";
|
||||
import storeConfig from "@/stores/config";
|
||||
import storePlaying from "@/stores/playing";
|
||||
import { type DetailedRom } from "@/stores/roms";
|
||||
import { formatTimestamp, getSupportedEJSCores } from "@/utils";
|
||||
@@ -21,13 +22,12 @@ import { getEmptyCoverImage } from "@/utils/covers";
|
||||
import CacheDialog from "@/views/Player/EmulatorJS/CacheDialog.vue";
|
||||
import Player from "@/views/Player/EmulatorJS/Player.vue";
|
||||
|
||||
const EMULATORJS_VERSION = "nightly";
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const { smAndDown } = useDisplay();
|
||||
const route = useRoute();
|
||||
const auth = storeAuth();
|
||||
const playingStore = storePlaying();
|
||||
const configStore = storeConfig();
|
||||
const { playing, fullScreen } = storeToRefs(playingStore);
|
||||
const rom = ref<DetailedRom | null>(null);
|
||||
const firmwareOptions = ref<FirmwareSchema[]>([]);
|
||||
@@ -42,7 +42,7 @@ const supportedCores = ref<string[]>([]);
|
||||
const gameRunning = ref(false);
|
||||
const fullScreenOnPlay = useLocalStorage("emulation.fullScreenOnPlay", true);
|
||||
|
||||
function onPlay() {
|
||||
async function onPlay() {
|
||||
if (rom.value && auth.scopes.includes("roms.user.write")) {
|
||||
romApi.updateUserRomProps({
|
||||
romId: rom.value.id,
|
||||
@@ -56,33 +56,44 @@ function onPlay() {
|
||||
fullScreen.value = fullScreenOnPlay.value;
|
||||
playing.value = true;
|
||||
|
||||
const LOCAL_PATH = "/assets/emulatorjs/data/";
|
||||
const CDN_PATH = `https://cdn.emulatorjs.org/${EMULATORJS_VERSION}/data/`;
|
||||
const EMULATORJS_VERSION = configStore.config.EJS_NETPLAY_ENABLED
|
||||
? "nightly"
|
||||
: "4.2.3";
|
||||
const LOCAL_PATH = "/assets/emulatorjs/data";
|
||||
const CDN_PATH = `https://cdn.emulatorjs.org/${EMULATORJS_VERSION}/data`;
|
||||
|
||||
// Try loading local loader.js via fetch to validate it's real JS
|
||||
fetch(`${LOCAL_PATH}loader.js`)
|
||||
.then((res) => {
|
||||
const type = res.headers.get("content-type") || "";
|
||||
if (!res.ok || !type.includes("javascript")) {
|
||||
throw new Error("Invalid local loader.js");
|
||||
}
|
||||
window.EJS_pathtodata = LOCAL_PATH;
|
||||
return res.text();
|
||||
})
|
||||
.then((jsCode) => {
|
||||
playing.value = true;
|
||||
fullScreen.value = fullScreenOnPlay.value;
|
||||
const script = document.createElement("script");
|
||||
script.textContent = jsCode;
|
||||
document.body.appendChild(script);
|
||||
})
|
||||
.catch(() => {
|
||||
console.warn("Local EmulatorJS failed, falling back to CDN");
|
||||
window.EJS_pathtodata = CDN_PATH;
|
||||
const fallbackScript = document.createElement("script");
|
||||
fallbackScript.src = `${CDN_PATH}loader.js`;
|
||||
document.body.appendChild(fallbackScript);
|
||||
function loadScript(src: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const s = document.createElement("script");
|
||||
s.src = src;
|
||||
s.async = true;
|
||||
s.onload = () => resolve();
|
||||
s.onerror = () => reject(new Error("Failed loading " + src));
|
||||
document.body.appendChild(s);
|
||||
});
|
||||
}
|
||||
|
||||
async function attemptLoad(path: string) {
|
||||
window.EJS_pathtodata = path;
|
||||
await loadScript(`${path}/loader.js`);
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
await attemptLoad(
|
||||
configStore.config.EJS_NETPLAY_ENABLED ? CDN_PATH : LOCAL_PATH,
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn("[Play] Local loader failed, trying CDN", e);
|
||||
await attemptLoad(
|
||||
configStore.config.EJS_NETPLAY_ENABLED ? LOCAL_PATH : CDN_PATH,
|
||||
);
|
||||
}
|
||||
playing.value = true;
|
||||
fullScreen.value = fullScreenOnPlay.value;
|
||||
} catch (err) {
|
||||
console.error("[Play] Emulator load failure:", err);
|
||||
}
|
||||
}
|
||||
|
||||
function onFullScreenChange() {
|
||||
|
||||
@@ -144,11 +144,16 @@ window.EJS_gameName = romRef.value.fs_name_no_tags
|
||||
window.EJS_language = selectedLanguage.value.value.replace("_", "-");
|
||||
window.EJS_disableAutoLang = true;
|
||||
|
||||
const { EJS_DEBUG, EJS_CACHE_LIMIT, EJS_NETPLAY_ICE_SERVERS } =
|
||||
configStore.config;
|
||||
|
||||
window.EJS_netplayServer = window.location.host;
|
||||
window.EJS_netplayICEServers = EJS_NETPLAY_ICE_SERVERS;
|
||||
const {
|
||||
EJS_DEBUG,
|
||||
EJS_CACHE_LIMIT,
|
||||
EJS_NETPLAY_ICE_SERVERS,
|
||||
EJS_NETPLAY_ENABLED,
|
||||
} = configStore.config;
|
||||
window.EJS_netplayServer = EJS_NETPLAY_ENABLED ? window.location.host : "";
|
||||
window.EJS_netplayICEServers = EJS_NETPLAY_ENABLED
|
||||
? EJS_NETPLAY_ICE_SERVERS
|
||||
: [];
|
||||
if (EJS_CACHE_LIMIT !== null) window.EJS_CacheLimit = EJS_CACHE_LIMIT;
|
||||
window.EJS_DEBUG_XX = EJS_DEBUG;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user