playsocketjs 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/playsocket-client.js +36 -3
- package/dist/playsocket-server.js +46 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -199,6 +199,7 @@ Creates a new PlaySocket Server instance with configuration options.
|
|
|
199
199
|
### Methods
|
|
200
200
|
|
|
201
201
|
- `stop`: Closes all active client connections, the websocket server and the underlying http server if it's standalone
|
|
202
|
+
- `kick(clientId: string, reason?: string)`: Kick a client by their clientID – this will close their connection and set an error message
|
|
202
203
|
- `onEvent(event: string, callback: Function)`: Register a server-side event callback
|
|
203
204
|
|
|
204
205
|
##### Event types
|
|
@@ -1754,7 +1754,8 @@ function requireCrdtManager () {
|
|
|
1754
1754
|
*/
|
|
1755
1755
|
importPropertyUpdate(data) {
|
|
1756
1756
|
try {
|
|
1757
|
-
const { key, operation, vectorClock } = data;
|
|
1757
|
+
const { key, operation: rawOperation, vectorClock } = data;
|
|
1758
|
+
const operation = this.#sanitizeValue(rawOperation);
|
|
1758
1759
|
if (this.#debug) console.log(CONSOLE_PREFIX + "Importing update:", data); // Debug
|
|
1759
1760
|
|
|
1760
1761
|
// Get COPY of current ops (or empty array if none yet)
|
|
@@ -1797,6 +1798,10 @@ function requireCrdtManager () {
|
|
|
1797
1798
|
*/
|
|
1798
1799
|
updateProperty(key, operation, value, updateValue) {
|
|
1799
1800
|
try {
|
|
1801
|
+
// Sanitize inputs
|
|
1802
|
+
value = this.#sanitizeValue(value);
|
|
1803
|
+
updateValue = this.#sanitizeValue(updateValue);
|
|
1804
|
+
|
|
1800
1805
|
// Debug log
|
|
1801
1806
|
if (this.#debug) console.log(CONSOLE_PREFIX + `Updating property with key ${key}, operation ${operation}, value ${value} and updateValue ${updateValue}.`);
|
|
1802
1807
|
|
|
@@ -1923,7 +1928,11 @@ function requireCrdtManager () {
|
|
|
1923
1928
|
}
|
|
1924
1929
|
}
|
|
1925
1930
|
|
|
1926
|
-
|
|
1931
|
+
/**
|
|
1932
|
+
* Sort by vector clock (causal order)
|
|
1933
|
+
* @param {Array} operations
|
|
1934
|
+
* @returns {Array} - Sorted operations
|
|
1935
|
+
*/
|
|
1927
1936
|
#sortByVectorClock(operations) {
|
|
1928
1937
|
return [...operations].sort((a, b) => {
|
|
1929
1938
|
const clockA = new Map(a.vectorClock || []);
|
|
@@ -1955,7 +1964,14 @@ function requireCrdtManager () {
|
|
|
1955
1964
|
});
|
|
1956
1965
|
}
|
|
1957
1966
|
|
|
1958
|
-
|
|
1967
|
+
/**
|
|
1968
|
+
* Handle an operation
|
|
1969
|
+
* @param {*} curValue
|
|
1970
|
+
* @param {string} operation
|
|
1971
|
+
* @param {*} value
|
|
1972
|
+
* @param {*} [updateValue]
|
|
1973
|
+
* @returns {*} - Value after the operation
|
|
1974
|
+
*/
|
|
1959
1975
|
#handleOperation(curValue, operation, value, updateValue) {
|
|
1960
1976
|
try {
|
|
1961
1977
|
// Clone to avoid reference issues
|
|
@@ -2002,6 +2018,18 @@ function requireCrdtManager () {
|
|
|
2002
2018
|
}
|
|
2003
2019
|
}
|
|
2004
2020
|
|
|
2021
|
+
/**
|
|
2022
|
+
* Remove HTML to prevent XSS
|
|
2023
|
+
* @param {Object} obj
|
|
2024
|
+
* @returns {Object} - Sanitized object
|
|
2025
|
+
*/
|
|
2026
|
+
#sanitizeValue(obj) {
|
|
2027
|
+
if (typeof obj === 'string') return (obj.includes('<') || obj.includes('>')) ? obj.replace(/[<>]/g, '') : obj;
|
|
2028
|
+
if (Array.isArray(obj)) return obj.map(item => this.#sanitizeValue(item));
|
|
2029
|
+
if (obj && typeof obj === 'object') return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, this.#sanitizeValue(v)]));
|
|
2030
|
+
return obj;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2005
2033
|
// Get property store
|
|
2006
2034
|
get getPropertyStore() {
|
|
2007
2035
|
try {
|
|
@@ -2295,6 +2323,11 @@ class PlaySocket {
|
|
|
2295
2323
|
this.destroy();
|
|
2296
2324
|
break;
|
|
2297
2325
|
|
|
2326
|
+
case 'kicked':
|
|
2327
|
+
this.#triggerEvent("error", `Kicked out of room: ${message.reason || "No reason provided."}`);
|
|
2328
|
+
this.destroy();
|
|
2329
|
+
break;
|
|
2330
|
+
|
|
2298
2331
|
case 'host_migrated':
|
|
2299
2332
|
this.#setHost(message.newHost);
|
|
2300
2333
|
break;
|
|
@@ -1880,7 +1880,8 @@ function requireCrdtManager () {
|
|
|
1880
1880
|
*/
|
|
1881
1881
|
importPropertyUpdate(data) {
|
|
1882
1882
|
try {
|
|
1883
|
-
const { key, operation, vectorClock } = data;
|
|
1883
|
+
const { key, operation: rawOperation, vectorClock } = data;
|
|
1884
|
+
const operation = this.#sanitizeValue(rawOperation);
|
|
1884
1885
|
if (this.#debug) console.log(CONSOLE_PREFIX + "Importing update:", data); // Debug
|
|
1885
1886
|
|
|
1886
1887
|
// Get COPY of current ops (or empty array if none yet)
|
|
@@ -1923,6 +1924,10 @@ function requireCrdtManager () {
|
|
|
1923
1924
|
*/
|
|
1924
1925
|
updateProperty(key, operation, value, updateValue) {
|
|
1925
1926
|
try {
|
|
1927
|
+
// Sanitize inputs
|
|
1928
|
+
value = this.#sanitizeValue(value);
|
|
1929
|
+
updateValue = this.#sanitizeValue(updateValue);
|
|
1930
|
+
|
|
1926
1931
|
// Debug log
|
|
1927
1932
|
if (this.#debug) console.log(CONSOLE_PREFIX + `Updating property with key ${key}, operation ${operation}, value ${value} and updateValue ${updateValue}.`);
|
|
1928
1933
|
|
|
@@ -2049,7 +2054,11 @@ function requireCrdtManager () {
|
|
|
2049
2054
|
}
|
|
2050
2055
|
}
|
|
2051
2056
|
|
|
2052
|
-
|
|
2057
|
+
/**
|
|
2058
|
+
* Sort by vector clock (causal order)
|
|
2059
|
+
* @param {Array} operations
|
|
2060
|
+
* @returns {Array} - Sorted operations
|
|
2061
|
+
*/
|
|
2053
2062
|
#sortByVectorClock(operations) {
|
|
2054
2063
|
return [...operations].sort((a, b) => {
|
|
2055
2064
|
const clockA = new Map(a.vectorClock || []);
|
|
@@ -2081,7 +2090,14 @@ function requireCrdtManager () {
|
|
|
2081
2090
|
});
|
|
2082
2091
|
}
|
|
2083
2092
|
|
|
2084
|
-
|
|
2093
|
+
/**
|
|
2094
|
+
* Handle an operation
|
|
2095
|
+
* @param {*} curValue
|
|
2096
|
+
* @param {string} operation
|
|
2097
|
+
* @param {*} value
|
|
2098
|
+
* @param {*} [updateValue]
|
|
2099
|
+
* @returns {*} - Value after the operation
|
|
2100
|
+
*/
|
|
2085
2101
|
#handleOperation(curValue, operation, value, updateValue) {
|
|
2086
2102
|
try {
|
|
2087
2103
|
// Clone to avoid reference issues
|
|
@@ -2128,6 +2144,18 @@ function requireCrdtManager () {
|
|
|
2128
2144
|
}
|
|
2129
2145
|
}
|
|
2130
2146
|
|
|
2147
|
+
/**
|
|
2148
|
+
* Remove HTML to prevent XSS
|
|
2149
|
+
* @param {Object} obj
|
|
2150
|
+
* @returns {Object} - Sanitized object
|
|
2151
|
+
*/
|
|
2152
|
+
#sanitizeValue(obj) {
|
|
2153
|
+
if (typeof obj === 'string') return (obj.includes('<') || obj.includes('>')) ? obj.replace(/[<>]/g, '') : obj;
|
|
2154
|
+
if (Array.isArray(obj)) return obj.map(item => this.#sanitizeValue(item));
|
|
2155
|
+
if (obj && typeof obj === 'object') return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, this.#sanitizeValue(v)]));
|
|
2156
|
+
return obj;
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2131
2159
|
// Get property store
|
|
2132
2160
|
get getPropertyStore() {
|
|
2133
2161
|
try {
|
|
@@ -2279,7 +2307,7 @@ function requireServer () {
|
|
|
2279
2307
|
|
|
2280
2308
|
// Generate client ID if none provided
|
|
2281
2309
|
if (!data.id) {
|
|
2282
|
-
const chars = '
|
|
2310
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789';
|
|
2283
2311
|
const maxAttempts = 50;
|
|
2284
2312
|
for (let i = 0; i < maxAttempts; i++) {
|
|
2285
2313
|
const id = Array.from({ length: 6 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
|
@@ -2587,6 +2615,19 @@ function requireServer () {
|
|
|
2587
2615
|
this.#callbacks.get(event).push(callback);
|
|
2588
2616
|
}
|
|
2589
2617
|
|
|
2618
|
+
/**
|
|
2619
|
+
* Kick a player from the server
|
|
2620
|
+
* @param {string} clientId - Client ID
|
|
2621
|
+
* @param {string} [reason] - Optional reason
|
|
2622
|
+
*/
|
|
2623
|
+
kick(clientId, reason) {
|
|
2624
|
+
const client = this.#clients.get(clientId);
|
|
2625
|
+
if (client) {
|
|
2626
|
+
client.send(encode({ type: 'kicked', reason }), { binary: true });
|
|
2627
|
+
client.close();
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2590
2631
|
/**
|
|
2591
2632
|
* Trigger an event to registered callbacks
|
|
2592
2633
|
* @private
|
|
@@ -2604,7 +2645,7 @@ function requireServer () {
|
|
|
2604
2645
|
stop() {
|
|
2605
2646
|
clearInterval(this.#heartbeatInterval);
|
|
2606
2647
|
if (this.#wss) {
|
|
2607
|
-
this.#
|
|
2648
|
+
this.#clients.forEach(client => {
|
|
2608
2649
|
client.send(encode({ type: 'server_stopped' }), { binary: true });
|
|
2609
2650
|
client.close();
|
|
2610
2651
|
});
|