Compare commits

...

4 Commits

Author SHA1 Message Date
peshomir 29e6c4f507 Update changelog and bump version 2025-12-30 21:41:53 +02:00
peshomir 1607255948 Automatic patches for the custom lobby version 2025-12-30 21:34:41 +02:00
peshomir dff5a9ad57 Improve propaganda block 2025-12-30 21:15:52 +02:00
peshomir bd67b39745 Update custom lobbies 2025-12-30 21:14:36 +02:00
8 changed files with 99 additions and 45 deletions

View File

@ -74,6 +74,9 @@ async function patchGameCode() {
code: `this.a = Math.floor(0.066 * b.c);
d = b.d - 4 * uiSizes.gap - this.a;`,
addToDictionary: ["uiSizes", "gap"]
}, {
code: `var dt=MenuManager.getState();if(dt===6){if(d===4211){/*...*/}}`,
addToDictionary: ["MenuManager", "getState"]
}];
codeSegments.forEach(({ code, addToDictionary }) => {
modUtils.matchCode(code, { addToDictionary })

View File

@ -214,4 +214,6 @@ export function definePatch(callback) {
* Helper for `modifyCode`
* @param {string} code
*/
export const insert = (code) => code.split(/\r?\n/g).map(l => "/*insert line:*/" + l).join("\n");
export const insert = (code) => (
"\n" + code.split(/\r?\n/g).map(l => "/*insert line:*/" + l).join("\n") + "\n"
);

View File

@ -1,10 +1,7 @@
import ModUtils from '../modUtils.js';
import ModUtils, { insert } from '../modUtils.js';
// Custom lobby patches
export default (/** @type {ModUtils} */ { insertCode, replaceCode, replaceRawCode, safeDictionary: dict, waitForMinification }) => {
// temporarily disabled for new versions
return;
export default (/** @type {ModUtils} */ { modifyCode, insertCode, replaceCode, replaceRawCode, safeDictionary: dict, waitForMinification }) => {
// set player id correctly
insertCode(`function aBG(aBE) {
@ -28,9 +25,24 @@ export default (/** @type {ModUtils} */ { insertCode, replaceCode, replaceRawCod
return __fx.customLobby.setActive(false);
}`)
insertCode(`this.send = function(a, b) {
if (a !== 0) {c(a);}
d[a].send(b);
}; /* here */`, "__fx.customLobby.setSendFunction(this.send)")
// when a socket error occurs on the custom lobby socket
insertCode(`this.b = function(id, t) { /* here */ this.a.push(t); if (i.h === 8 && id === 0) {if (t === 4211) {f(t);} else {/*...*/}}};`, `id===1 && __fx.customLobby.isActive() && MenuManager.getState() !== 6 && __fx.customLobby.setActive(false);`, { dictionary: {MenuManager: dict.MenuManager, getState: dict.getState} })
// when leaving a game
modifyCode(`${insert(`if (__fx.customLobby.isActive() === false)`)} a.b.c();
d.e();
this.f = 0;
g.h();
i.j.setState(0);
MenuManager.setState(0);
${insert(`if (!__fx.customLobby.isActive()) `)} k.l.m(n);
${insert(`if (__fx.customLobby.isActive()) __fx.customLobby.rejoinLobby();
else`)} if (this.o === 2) {/*...*/}`)
waitForMinification(() => {
replaceRawCode("this.send=function(socketId,data){aJE(socketId),aJ4[socketId].send(data)}",
"this.send=function(socketId,data){aJE(socketId),aJ4[socketId].send(data)},__fx.customLobby.setSendFunction(this.send)")
replaceRawCode("b7.dH(a0),0===b7.size?aq.kt.aJJ(wR,3205):",
"b7.dH(a0),0===b7.size?aq.kt.aJJ(wR,3205):__fx.customLobby.isCustomMessage(a0)||")
// set the custom lobby to inactive when clicking the "Back" button on the connection screen or leaving the lobby
@ -39,19 +51,6 @@ export default (/** @type {ModUtils} */ { insertCode, replaceCode, replaceRawCod
replaceRawCode("function(){n.r(),bl.zf(),Sockets.s.ze(3240),n.o(5,5)}",
`(__fx.customLobby.setLeaveFunction(() => {n.r(),bl.zf(),Sockets.s.ze(3240),__fx.customLobby.setActive(false),n.o(5,5)}),
function(){n.r(),bl.zf(),Sockets.s.ze(3240),__fx.customLobby.setActive(false),n.o(5,5)})`)
// when a socket error occurs on the custom lobby socket
// TODO: Fix these after main WebSocket fix is confirmed working
/*
replaceRawCode("this.wQ=function(wR,d){if(8===i.pz&&0===wR)if(4211===d)wS(d);",
`this.wQ=function(wR,d){
wR===1 && __fx.customLobby.isActive() && ${dict.MenuManager}.${dict.getState}() !== 6 && __fx.customLobby.setActive(false);
if(8===i.pz&&0===wR)if(4211===d)wS(d);`)
// when leaving a game
replaceRawCode("this.wl=function(zs){a1.gZ||az.oO.a11.length||(az.oO.a11=az.a12.vd()),ap.ky.zt(),this.vH=0,bU.zu(),m.n.setState(0),aN.setState(0),zs||bJ.df.show(),2===this.a3D?i.ky.a3U():1===this.a3D?i.j(19):i.j(5,5)}",
`this.wl=function(zs){a1.gZ||az.oO.a11.length||(az.oO.a11=az.a12.vd()),
__fx.customLobby.isActive() === false && ap.ky.zt(),
this.vH=0,bU.zu(),m.n.setState(0),aN.setState(0),zs||bJ.df.show();
if (__fx.customLobby.isActive()) __fx.customLobby.rejoinLobby(); else 2===this.a3D?i.ky.a3U():1===this.a3D?i.j(19):i.j(5,5)}`)
// do not display lobby UI
replaceRawCode(`(sV.style.backdropFilter="blur(4px)",sV.style.webkitBackdropFilter="blur(4px)"),`,
`(sV.style.backdropFilter="blur(4px)",sV.style.webkitBackdropFilter="blur(4px)"),
@ -80,6 +79,5 @@ export default (/** @type {ModUtils} */ { insertCode, replaceCode, replaceRawCod
replaceRawCode("1===a.b?this.gLobbyMaxJoin=this.gHumans:this.gLobbyMaxJoin=this.data.playerCount,this.maxPlayers=this.gLobbyMaxJoin,this.gBots=this.gLobbyMaxJoin-this.gHumans,this.sg=0,",
`this.gLobbyMaxJoin = __fx.customLobby.isActive() ? Math.max(Math.min(__fx.customLobby.gameInfo.botCount, this.data.playerCount), this.gHumans) : 1===a.b?this.gLobbyMaxJoin=this.gHumans:this.gLobbyMaxJoin=this.data.playerCount,
this.maxPlayers=this.gLobbyMaxJoin,this.gBots=this.gLobbyMaxJoin-this.gHumans,this.sg=0,`)
*/
});
}

View File

@ -1,12 +1,26 @@
import { definePatch } from "../modUtils.js"
import { definePatch, insert } from "../modUtils.js"
import { styleText } from "node:util";
export default definePatch(({ insertCode }) => {
export default definePatch(({ insertCode, modifyCode }) => {
// Hide propaganda popup
insertCode(`/* here */
a = b.c + 60 * 1000;
(new ea()).show(eS.eb, eS.colors, eS.id);
eS = null;
return true;`, `if (__fx.settings.hidePropagandaPopup) return;`)
return true;`, `if (__fx.settings.hidePropagandaPopup || __fx.customLobby.isActive()) return;`)
modifyCode(`if (!a.b.c(0)) {
d = e.f + 1000 * 1;
return;
} ${insert(`if (!__fx.settings.hidePropagandaPopup && !__fx.customLobby.isActive())`)} a.g.h(5);`)
// for the custom lobby version
try {
modifyCode(`new a("⚔️<br>" + __L(), function() {
${insert(`if (__fx.isCustomLobbyVersion) alert("This version is for use with custom lobbies only. For normal multiplayer, use the version at https://fxclient.github.io/FXclient/")
else`)} b(0);
}, ${insert(`__fx.isCustomLobbyVersion ? "rgba(50, 50, 50, 0.6)" : `)} c.d)`)
} catch (error) {
console.warn(styleText("yellow", `Warning: failed to apply patches specific to the custom lobby version`))
}
})

View File

@ -1,12 +1,13 @@
import WindowManager from "./windowManager.js";
const customLobbiesUnavailable = true;
//const socketURL = "ws://localhost:8080/";
const socketURL = "wss://fx.peshomir.workers.dev/";
const customLobbiesUnavailable = false;
// const socketURL = "ws://localhost:8080/";
const socketURL = "wss://fx-lobbies.peshomir.workers.dev/";
const customMessageMarker = 120;
let isActive = false;
let currentCode = "";
let joinLobby = () => { };
/** when `leaveLobby` is called, the modified code will also execute setActive(false) */
let leaveLobby = () => { };
let sendRaw = (socketId, data) => { };
const textEncoder = new TextEncoder();
@ -33,6 +34,10 @@ const windowElement = WindowManager.create({
const header = document.createElement("h2");
header.textContent = "Custom Lobby";
header.style.marginBottom = "0px";
header.style.marginBlockStart = "0.5em";
const pingIndicator = document.createElement("p");
pingIndicator.style.marginTop = "0px";
const main = document.createElement("div");
main.className = "customlobby-main";
@ -152,7 +157,7 @@ const copyLinkButton = createButton("Copy link", () => {
});
footer.append(startButton, leaveButton, copyLinkButton);
windowElement.append(header, main, footer);
windowElement.append(header, pingIndicator, main, footer);
/** @param {HTMLSelectElement} element */
function setSelectMenuOptions(options, element) {
@ -174,13 +179,13 @@ document.getElementById("lobbyCode").addEventListener("input", ({ target: input
currentCode = input.value.toLowerCase();
input.value = "";
WindowManager.closeWindow("lobbyJoinMenu");
isActive = true;
setActive(true)
joinLobby();
});
document.getElementById("createLobbyButton").addEventListener("click", () => {
currentCode = "";
WindowManager.closeWindow("lobbyJoinMenu");
isActive = true;
setActive(true)
joinLobby();
});
@ -204,7 +209,12 @@ function isCustomMessage(raw) {
const subArray = new Uint8Array(raw.buffer, 1);
const message = JSON.parse(textDecoder.decode(subArray));
const { t: type, d: data } = message;
if (type === "lobby") {
if (type === "pong") {
const latency = performance.now() - data
pingIndicator.innerText = `Ping: ${latency} ms`
} else if (type === "lobby") {
pingIndicator.innerText = ""
sendPing()
WindowManager.openWindow("customLobby");
header.textContent = "Custom Lobby " + data.code;
currentCode = data.code;
@ -293,7 +303,9 @@ function updatePlayerCount() {
}
function getSocketURL() {
return socketURL + (currentCode === "" ? "create" : "join?" + currentCode)
if (currentCode !== "") return socketURL + "join?" + currentCode
const region = document.getElementById("customLobbyRegion").value
return socketURL + "create" + (region === "default" ? "" : `?location=${region}`)
}
function getPlayerId() {
let id = 0;
@ -317,7 +329,7 @@ function checkForLobbyLink(isHashChangeEvent) {
// in case the player is already in a lobby
if (isHashChangeEvent) leaveLobby();
currentCode = hash.slice(7);
isActive = true;
setActive(true)
joinLobby();
}
}
@ -329,9 +341,16 @@ function setJoinFunction(f) {
}
function setLeaveFunction(f) { leaveLobby = f; }
function setSendFunction(f) { sendRaw = f; }
function sendPing() {
sendMessage("ping", performance.now())
}
let pingInterval;
function setActive(active) {
isActive = active;
if (active === false) WindowManager.closeWindow("customLobby");
if (active === false) {
WindowManager.closeWindow("customLobby");
if (pingInterval !== undefined) clearInterval(pingInterval)
} else pingInterval = setInterval(sendPing, 10_000)
}
function hideWindow() {
WindowManager.closeWindow("customLobby");

View File

@ -14,15 +14,16 @@ import customLobby from './customLobby.js';
import { displayChangelog } from './changelog.js';
import { reportError } from './debugging.js';
const savedVersion = localStorage.getItem("fx_version");
if (savedVersion !== version) {
localStorage.setItem("fx_version", version);
if (savedVersion !== null) displayChangelog();
}
window.__fx = window.__fx || {};
const __fx = window.__fx;
__fx.version = version + " " + lastUpdated;
__fx.isCustomLobbyVersion = window.location.href.startsWith("https://fxclient.github.io/custom-lobbies")
const savedVersion = localStorage.getItem("fx_version");
if (savedVersion !== version && !__fx.isCustomLobbyVersion) {
localStorage.setItem("fx_version", version);
if (savedVersion !== null) displayChangelog();
}
__fx.settingsManager = settingsManager;
__fx.leaderboardFilter = leaderboardFilter;

View File

@ -112,7 +112,20 @@
<div class="window flex-column" id="customLobbyJoinMenu" style="display: none">
<input type="text" id="lobbyCode" placeholder="Enter lobby code">
or
<button id="createLobbyButton">Create new lobby</button>
<div>
<button id="createLobbyButton">Create new lobby</button>
<label for="region">in region:</label>
<select id="customLobbyRegion" name="region"
title="Note: Region selection is a best effort, not a guarantee. The regions listed are only a subset of all the possible locations where a lobby can be created.">
<option value="default" selected>Automatic (closest to you)</option>
<option value="wnam">Western North America</option>
<option value="enam">Eastern North America</option>
<option value="weur">Western Europe</option>
<option value="eeur">Eastern Europe</option>
<option value="apac">Asia-Pacific</option>
<option value="oc">Oceania</option>
</select>
</div>
</div>
<div class="window scrollable selectable" id="playerlist" style="display: none;">
<h1>Player List</h1>

View File

@ -1,7 +1,11 @@
{
"version": "0.6.17",
"lastUpdated": "Dec 4",
"version": "0.6.18",
"lastUpdated": "Dec 30",
"changes": [
"Fixes for game update v2.14.4"
"Updated custom lobbies to the latest version",
"When creating a custom lobby, you can now select a region where it will be created",
"The latency to the custom lobby server is displayed below the header in the custom lobby UI",
"Improved the propaganda blocking feature",
"Added automatic patches for the special custom lobby version (https://fxclient.github.io/custom-lobbies) which continues to serve its purpose of guaranteeing access to custom lobbies while allowing the regular version to quickly be updated to the latest Territorial.io version, neglecting the custom lobby patches if needed"
]
}