Created
April 2, 2026 17:46
-
-
Save andronedev/ebc745a8df9d28af8fa0fa4c40916746 to your computer and use it in GitHub Desktop.
drifted king Region Switcher userscript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Drift King - Region Switcher | |
| // @namespace drifted-bot | |
| // @version 2.1 | |
| // @description Affiche et permet de changer la région, le serveur et la room | |
| // @match https://www.drifted.com/* | |
| // @grant none | |
| // @run-at document-start | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| const origWS = WebSocket; | |
| const info = { region: null, room: null, master: null, game: null }; | |
| let forcedRegion = null; | |
| const REGION_LIST = [ | |
| { code: 'eu', name: 'Europe', flag: '🇪🇺' }, | |
| { code: 'us', name: 'US East', flag: '🇺🇸' }, | |
| { code: 'usw', name: 'US West', flag: '🇺🇸' }, | |
| { code: 'ussc', name: 'US South', flag: '🇺🇸' }, | |
| { code: 'cae', name: 'Canada', flag: '🇨🇦' }, | |
| { code: 'sa', name: 'South America', flag: '🇧🇷' }, | |
| { code: 'asia', name: 'Asia', flag: '🌏' }, | |
| { code: 'jp', name: 'Japan', flag: '🇯🇵' }, | |
| { code: 'kr', name: 'South Korea', flag: '🇰🇷' }, | |
| { code: 'hk', name: 'Hong Kong', flag: '🇭🇰' }, | |
| { code: 'au', name: 'Australia', flag: '🇦🇺' }, | |
| { code: 'in', name: 'India', flag: '🇮🇳' }, | |
| { code: 'ru', name: 'Russia', flag: '🇷🇺' }, | |
| { code: 'rue', name: 'Russia East', flag: '🇷🇺' }, | |
| { code: 'tr', name: 'Turkey', flag: '🇹🇷' }, | |
| { code: 'uae', name: 'UAE', flag: '🇦🇪' }, | |
| { code: 'za', name: 'South Africa', flag: '🇿🇦' }, | |
| ]; | |
| const REGION_NAMES = {}; | |
| const REGION_FLAGS = {}; | |
| REGION_LIST.forEach(r => { REGION_NAMES[r.code] = r.name; REGION_FLAGS[r.code] = r.flag; }); | |
| const SERVER_REGIONS = { | |
| GCAMS: 'eu', GCASH: 'us', GCTOK: 'jp', GCSYD: 'au', GCSP: 'sa', | |
| GCIST: 'tr', GCDXB: 'uae', GCHK: 'hk', GCLUX: 'eu', | |
| }; | |
| // ========== PROTOCOL ========== | |
| function readCompactString(arr, offset) { | |
| const first = arr[offset]; | |
| let len, start; | |
| if (first < 128) { len = first; start = offset + 1; } | |
| else { len = (first & 0x7F) | (arr[offset + 1] << 7); start = offset + 2; } | |
| return { str: String.fromCharCode.apply(null, arr.slice(start, start + len)), end: start + len }; | |
| } | |
| function findString(arr, key) { | |
| for (var i = 0; i < arr.length - 3; i++) { | |
| if (arr[i] === key && arr[i + 1] === 0x07) { | |
| var r = readCompactString(arr, i + 2); | |
| if (r.str && /^[\x20-\x7e]+$/.test(r.str)) return r; | |
| } | |
| } | |
| return null; | |
| } | |
| function replaceRegionInPacket(data, newRegion) { | |
| var arr = new Uint8Array(data); | |
| for (var i = 0; i < arr.length - 3; i++) { | |
| if (arr[i] === 0xD2 && arr[i + 1] === 0x07) { | |
| var parsed = readCompactString(arr, i + 2); | |
| if (!parsed.str || parsed.str.length > 5) continue; | |
| var newBytes = new TextEncoder().encode(newRegion); | |
| var before = arr.slice(0, i + 2); | |
| var after = arr.slice(parsed.end); | |
| var result = new Uint8Array(before.length + 1 + newBytes.length + after.length); | |
| var off = 0; | |
| result.set(before, off); off += before.length; | |
| result[off++] = newBytes.length; | |
| result.set(newBytes, off); off += newBytes.length; | |
| result.set(after, off); | |
| console.log('[DK] Region: ' + parsed.str + ' → ' + newRegion); | |
| return result.buffer; | |
| } | |
| } | |
| return data; | |
| } | |
| function detectRegionFromURL(url) { | |
| for (var prefix in SERVER_REGIONS) { | |
| if (url.toUpperCase().indexOf(prefix) !== -1) return SERVER_REGIONS[prefix]; | |
| } | |
| return null; | |
| } | |
| // ========== UI ========== | |
| var overlay, dropdown; | |
| function createUI() { | |
| if (overlay) return; | |
| overlay = document.createElement('div'); | |
| overlay.style.cssText = 'position:fixed;top:8px;left:50%;transform:translateX(-50%);z-index:2147483647;background:rgba(0,0,0,.92);color:#0f0;font:bold 13px/1.4 Consolas,Monaco,monospace;padding:7px 16px;border-radius:8px;border:2px solid rgba(0,255,0,.3);display:flex;gap:14px;align-items:center;user-select:none;pointer-events:auto'; | |
| document.body.appendChild(overlay); | |
| dropdown = document.createElement('div'); | |
| dropdown.style.cssText = 'position:fixed;top:42px;left:50%;transform:translateX(-50%);z-index:2147483647;background:rgba(8,8,8,.97);color:#ccc;font:12px Consolas,Monaco,monospace;border-radius:8px;border:1px solid rgba(0,255,0,.2);display:none;flex-direction:column;max-height:400px;overflow-y:auto;min-width:230px;box-shadow:0 8px 32px rgba(0,0,0,.7)'; | |
| var title = document.createElement('div'); | |
| title.style.cssText = 'padding:8px 14px;color:#0f0;font-size:13px;border-bottom:1px solid rgba(0,255,0,.1);font-weight:bold'; | |
| title.textContent = 'Changer de région'; | |
| dropdown.appendChild(title); | |
| REGION_LIST.forEach(function (r) { | |
| var item = document.createElement('div'); | |
| item.style.cssText = 'padding:7px 14px;cursor:pointer;display:flex;gap:10px;transition:background .15s'; | |
| item.innerHTML = '<span style="font-size:15px">' + r.flag + '</span><span style="color:#888;width:35px">' + r.code + '</span><span>' + r.name + '</span>'; | |
| item.onmouseenter = function () { item.style.background = 'rgba(0,255,0,.1)'; }; | |
| item.onmouseleave = function () { item.style.background = 'transparent'; }; | |
| item.onclick = function () { selectRegion(r.code); }; | |
| dropdown.appendChild(item); | |
| }); | |
| var autoItem = document.createElement('div'); | |
| autoItem.style.cssText = 'padding:8px 14px;cursor:pointer;border-top:1px solid rgba(0,255,0,.1);color:#888;transition:background .15s'; | |
| autoItem.textContent = '↩ Auto (défaut du jeu)'; | |
| autoItem.onmouseenter = function () { autoItem.style.background = 'rgba(255,255,255,.05)'; }; | |
| autoItem.onmouseleave = function () { autoItem.style.background = 'transparent'; }; | |
| autoItem.onclick = function () { selectRegion(null); }; | |
| dropdown.appendChild(autoItem); | |
| document.body.appendChild(dropdown); | |
| document.addEventListener('click', function (e) { | |
| if (!dropdown.contains(e.target) && !overlay.contains(e.target)) { | |
| dropdown.style.display = 'none'; | |
| } | |
| }); | |
| updateUI(); | |
| } | |
| function selectRegion(code) { | |
| forcedRegion = code; | |
| dropdown.style.display = 'none'; | |
| updateUI(); | |
| if (code) { | |
| showToast(REGION_FLAGS[code] + ' ' + code.toUpperCase() + ' (' + REGION_NAMES[code] + ')\nRetourne au menu → PLAY → Online'); | |
| } else { | |
| showToast('Région → Auto'); | |
| } | |
| } | |
| function showToast(msg) { | |
| var t = document.createElement('div'); | |
| t.style.cssText = 'position:fixed;bottom:60px;left:50%;transform:translateX(-50%);z-index:2147483647;background:rgba(0,0,0,.92);color:#0f0;font:13px Consolas,monospace;padding:12px 24px;border-radius:8px;border:1px solid rgba(0,255,0,.2);white-space:pre-line;text-align:center'; | |
| t.textContent = msg; | |
| document.body.appendChild(t); | |
| setTimeout(function () { | |
| t.style.transition = 'opacity .5s'; | |
| t.style.opacity = '0'; | |
| setTimeout(function () { t.remove(); }, 500); | |
| }, 3000); | |
| } | |
| function updateUI() { | |
| if (!overlay) return; | |
| var r = info.region; | |
| var flag = r ? (REGION_FLAGS[r] || '🌐') : '🌐'; | |
| var name = r ? (REGION_NAMES[r] || '?') : '—'; | |
| var room = info.room || '—'; | |
| var srv = info.game ? info.game.replace(/^wss?:\/\//, '').replace(/\.exitgames.*/, '') : '—'; | |
| var forced = forcedRegion ? ' <span style="color:#ff0;font-size:11px">→ ' + (REGION_FLAGS[forcedRegion] || '') + ' ' + forcedRegion.toUpperCase() + '</span>' : ''; | |
| overlay.innerHTML = | |
| '<span id="dk-rbtn" style="cursor:pointer;padding:2px 6px;border-radius:4px;transition:background .2s">' + | |
| flag + ' <b>' + (r ? r.toUpperCase() : '—') + '</b> ' + | |
| '<span style="color:#999;font-size:11px">' + name + '</span>' + forced + | |
| ' <span style="color:#555;font-size:10px">▼</span></span>' + | |
| '<span style="color:#333">│</span>' + | |
| '<span>🏠 <b>' + room + '</b></span>' + | |
| '<span style="color:#333">│</span>' + | |
| '<span style="color:#888">🖥 ' + srv + '</span>'; | |
| var btn = document.getElementById('dk-rbtn'); | |
| if (btn) { | |
| btn.onmouseenter = function () { btn.style.background = 'rgba(0,255,0,.15)'; }; | |
| btn.onmouseleave = function () { btn.style.background = 'transparent'; }; | |
| btn.onclick = function (e) { | |
| e.stopPropagation(); | |
| dropdown.style.display = dropdown.style.display === 'flex' ? 'none' : 'flex'; | |
| }; | |
| } | |
| } | |
| // ========== WS HOOK ========== | |
| window.WebSocket = function (url, protocols) { | |
| var ws = protocols ? new origWS(url, protocols) : new origWS(url); | |
| var isNS = url.indexOf('photonengine.io') !== -1; | |
| if (url.indexOf('/Master') !== -1) { | |
| info.master = url.split('?')[0]; | |
| var r = detectRegionFromURL(url); | |
| if (r) info.region = r; | |
| } | |
| if (url.indexOf('/game') !== -1) { | |
| info.game = url.split('?')[0]; | |
| var r2 = detectRegionFromURL(url); | |
| if (r2) info.region = r2; | |
| } | |
| updateUI(); | |
| var origSend = ws.send.bind(ws); | |
| ws.send = function (data) { | |
| if (data instanceof ArrayBuffer) { | |
| var arr = new Uint8Array(data); | |
| if (arr[1] === 0x02) { | |
| // Auth on NameServer — replace region if forced | |
| if (arr[2] === 0xE6 && isNS && forcedRegion) { | |
| var ri = findString(arr, 0xD2); | |
| if (ri && ri.str.length <= 5) { | |
| data = replaceRegionInPacket(data, forcedRegion); | |
| info.region = forcedRegion; | |
| updateUI(); | |
| } | |
| } | |
| // Detect current region | |
| if (arr[2] === 0xE6) { | |
| var ri2 = findString(new Uint8Array(data), 0xD2); | |
| if (ri2 && ri2.str.length <= 5) { info.region = ri2.str; updateUI(); } | |
| } | |
| // Detect room | |
| if (arr[2] === 0xE2) { | |
| var ri3 = findString(arr, 0xFF); | |
| if (ri3) { info.room = ri3.str; updateUI(); } | |
| } | |
| } | |
| } | |
| return origSend(data); | |
| }; | |
| ws.addEventListener('message', function (evt) { | |
| if (!(evt.data instanceof ArrayBuffer)) return; | |
| var arr = new Uint8Array(evt.data); | |
| if (arr[1] === 0x03) { | |
| var ai = findString(arr, 0xE6); | |
| if (ai && ai.str.indexOf('exitgames.com') !== -1) { | |
| if (ai.str.indexOf('/game') !== -1) info.game = ai.str; | |
| else if (ai.str.indexOf('/Master') !== -1) info.master = ai.str; | |
| var r = detectRegionFromURL(ai.str); | |
| if (r) info.region = r; | |
| updateUI(); | |
| } | |
| } | |
| }); | |
| return ws; | |
| }; | |
| window.WebSocket.prototype = origWS.prototype; | |
| window.WebSocket.CONNECTING = 0; | |
| window.WebSocket.OPEN = 1; | |
| window.WebSocket.CLOSING = 2; | |
| window.WebSocket.CLOSED = 3; | |
| // ========== INIT ========== | |
| function init() { | |
| if (document.body) { createUI(); } | |
| else { document.addEventListener('DOMContentLoaded', createUI); } | |
| } | |
| init(); | |
| window.__dkInfo = info; | |
| window.__dkSetRegion = function (r) { selectRegion(r); }; | |
| console.log('[DK] Region Switcher v2.1 loaded'); | |
| })(); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://www.drifted.com/gameframe/335183/