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); code: `this.a = Math.floor(0.066 * b.c);
d = b.d - 4 * uiSizes.gap - this.a;`, d = b.d - 4 * uiSizes.gap - this.a;`,
addToDictionary: ["uiSizes", "gap"] addToDictionary: ["uiSizes", "gap"]
}, {
code: `var dt=MenuManager.getState();if(dt===6){if(d===4211){/*...*/}}`,
addToDictionary: ["MenuManager", "getState"]
}]; }];
codeSegments.forEach(({ code, addToDictionary }) => { codeSegments.forEach(({ code, addToDictionary }) => {
modUtils.matchCode(code, { addToDictionary }) modUtils.matchCode(code, { addToDictionary })

View File

@ -214,4 +214,6 @@ export function definePatch(callback) {
* Helper for `modifyCode` * Helper for `modifyCode`
* @param {string} code * @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 // Custom lobby patches
export default (/** @type {ModUtils} */ { insertCode, replaceCode, replaceRawCode, safeDictionary: dict, waitForMinification }) => { export default (/** @type {ModUtils} */ { modifyCode, insertCode, replaceCode, replaceRawCode, safeDictionary: dict, waitForMinification }) => {
// temporarily disabled for new versions
return;
// set player id correctly // set player id correctly
insertCode(`function aBG(aBE) { insertCode(`function aBG(aBE) {
@ -28,9 +25,24 @@ export default (/** @type {ModUtils} */ { insertCode, replaceCode, replaceRawCod
return __fx.customLobby.setActive(false); 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(() => { 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):", 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)||") "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 // 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)}", 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)}), `(__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)})`) 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 // do not display lobby UI
replaceRawCode(`(sV.style.backdropFilter="blur(4px)",sV.style.webkitBackdropFilter="blur(4px)"),`, replaceRawCode(`(sV.style.backdropFilter="blur(4px)",sV.style.webkitBackdropFilter="blur(4px)"),`,
`(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,", 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.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,`) 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 // Hide propaganda popup
insertCode(`/* here */ insertCode(`/* here */
a = b.c + 60 * 1000; a = b.c + 60 * 1000;
(new ea()).show(eS.eb, eS.colors, eS.id); (new ea()).show(eS.eb, eS.colors, eS.id);
eS = null; 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"; import WindowManager from "./windowManager.js";
const customLobbiesUnavailable = true; const customLobbiesUnavailable = false;
//const socketURL = "ws://localhost:8080/"; // const socketURL = "ws://localhost:8080/";
const socketURL = "wss://fx.peshomir.workers.dev/"; const socketURL = "wss://fx-lobbies.peshomir.workers.dev/";
const customMessageMarker = 120; const customMessageMarker = 120;
let isActive = false; let isActive = false;
let currentCode = ""; let currentCode = "";
let joinLobby = () => { }; let joinLobby = () => { };
/** when `leaveLobby` is called, the modified code will also execute setActive(false) */
let leaveLobby = () => { }; let leaveLobby = () => { };
let sendRaw = (socketId, data) => { }; let sendRaw = (socketId, data) => { };
const textEncoder = new TextEncoder(); const textEncoder = new TextEncoder();
@ -33,6 +34,10 @@ const windowElement = WindowManager.create({
const header = document.createElement("h2"); const header = document.createElement("h2");
header.textContent = "Custom Lobby"; 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"); const main = document.createElement("div");
main.className = "customlobby-main"; main.className = "customlobby-main";
@ -152,7 +157,7 @@ const copyLinkButton = createButton("Copy link", () => {
}); });
footer.append(startButton, leaveButton, copyLinkButton); footer.append(startButton, leaveButton, copyLinkButton);
windowElement.append(header, main, footer); windowElement.append(header, pingIndicator, main, footer);
/** @param {HTMLSelectElement} element */ /** @param {HTMLSelectElement} element */
function setSelectMenuOptions(options, element) { function setSelectMenuOptions(options, element) {
@ -174,13 +179,13 @@ document.getElementById("lobbyCode").addEventListener("input", ({ target: input
currentCode = input.value.toLowerCase(); currentCode = input.value.toLowerCase();
input.value = ""; input.value = "";
WindowManager.closeWindow("lobbyJoinMenu"); WindowManager.closeWindow("lobbyJoinMenu");
isActive = true; setActive(true)
joinLobby(); joinLobby();
}); });
document.getElementById("createLobbyButton").addEventListener("click", () => { document.getElementById("createLobbyButton").addEventListener("click", () => {
currentCode = ""; currentCode = "";
WindowManager.closeWindow("lobbyJoinMenu"); WindowManager.closeWindow("lobbyJoinMenu");
isActive = true; setActive(true)
joinLobby(); joinLobby();
}); });
@ -204,7 +209,12 @@ function isCustomMessage(raw) {
const subArray = new Uint8Array(raw.buffer, 1); const subArray = new Uint8Array(raw.buffer, 1);
const message = JSON.parse(textDecoder.decode(subArray)); const message = JSON.parse(textDecoder.decode(subArray));
const { t: type, d: data } = message; 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"); WindowManager.openWindow("customLobby");
header.textContent = "Custom Lobby " + data.code; header.textContent = "Custom Lobby " + data.code;
currentCode = data.code; currentCode = data.code;
@ -293,7 +303,9 @@ function updatePlayerCount() {
} }
function getSocketURL() { 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() { function getPlayerId() {
let id = 0; let id = 0;
@ -317,7 +329,7 @@ function checkForLobbyLink(isHashChangeEvent) {
// in case the player is already in a lobby // in case the player is already in a lobby
if (isHashChangeEvent) leaveLobby(); if (isHashChangeEvent) leaveLobby();
currentCode = hash.slice(7); currentCode = hash.slice(7);
isActive = true; setActive(true)
joinLobby(); joinLobby();
} }
} }
@ -329,9 +341,16 @@ function setJoinFunction(f) {
} }
function setLeaveFunction(f) { leaveLobby = f; } function setLeaveFunction(f) { leaveLobby = f; }
function setSendFunction(f) { sendRaw = f; } function setSendFunction(f) { sendRaw = f; }
function sendPing() {
sendMessage("ping", performance.now())
}
let pingInterval;
function setActive(active) { function setActive(active) {
isActive = 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() { function hideWindow() {
WindowManager.closeWindow("customLobby"); WindowManager.closeWindow("customLobby");

View File

@ -14,15 +14,16 @@ import customLobby from './customLobby.js';
import { displayChangelog } from './changelog.js'; import { displayChangelog } from './changelog.js';
import { reportError } from './debugging.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 || {}; window.__fx = window.__fx || {};
const __fx = window.__fx; const __fx = window.__fx;
__fx.version = version + " " + lastUpdated; __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.settingsManager = settingsManager;
__fx.leaderboardFilter = leaderboardFilter; __fx.leaderboardFilter = leaderboardFilter;

View File

@ -112,7 +112,20 @@
<div class="window flex-column" id="customLobbyJoinMenu" style="display: none"> <div class="window flex-column" id="customLobbyJoinMenu" style="display: none">
<input type="text" id="lobbyCode" placeholder="Enter lobby code"> <input type="text" id="lobbyCode" placeholder="Enter lobby code">
or 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>
<div class="window scrollable selectable" id="playerlist" style="display: none;"> <div class="window scrollable selectable" id="playerlist" style="display: none;">
<h1>Player List</h1> <h1>Player List</h1>

View File

@ -1,7 +1,11 @@
{ {
"version": "0.6.17", "version": "0.6.18",
"lastUpdated": "Dec 4", "lastUpdated": "Dec 30",
"changes": [ "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"
] ]
} }