Powerline Server Switcher

Server switcher for powerline

当前为 2024-08-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Powerline Server Switcher
  3. // @author Rumini - Discord: rumini & zaynbieber
  4. // @description Server switcher for powerline
  5. // @version 1.0
  6. // @match *://powerline.io/*
  7. // @icon https://i.imgur.com/9k4SFr0.png
  8. // @license MIT
  9. // @grant none
  10. // @run-at document-start
  11. // @namespace https://greasyfork.org/users/1356205
  12. // ==/UserScript==
  13.  
  14. if (window.location.href === 'https://powerline.io/') {
  15. window.location.href = 'https://powerline.io/maindev.html';
  16. }
  17.  
  18. (function () {
  19. 'use strict';
  20.  
  21. // Configuration
  22. const CONFIG = {
  23. regions: ['Europe', 'Asia', 'America'],
  24. countryCodes: { Europe: 'DE', Asia: 'JP', America: 'US' },
  25. euCountries: new Set(['AL', 'AD', 'AT', 'BY', 'BE', 'BA', 'BG', 'HR', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IS', 'IE', 'IT', 'LV', 'LI', 'LT', 'LU', 'MT', 'MC', 'NL', 'NO', 'PL', 'PT', 'RO', 'RU', 'SM', 'RS', 'SK', 'SI', 'ES', 'SE', 'CH', 'UA', 'GB', 'VA']),
  26. asiaCountries: new Set(['AF', 'AM', 'AZ', 'BH', 'BD', 'BT', 'BN', 'KH', 'CN', 'CY', 'GE', 'IN', 'ID', 'IR', 'IQ', 'IL', 'JP', 'JO', 'KZ', 'KW', 'KG', 'LA', 'LB', 'MY', 'MV', 'MN', 'MM', 'NP', 'KP', 'OM', 'PK', 'PS', 'PH', 'QA', 'SA', 'SG', 'KR', 'LK', 'SY', 'TW', 'TJ', 'TH', 'TR', 'TM', 'AE', 'UZ', 'VN', 'YE']),
  27. updateInterval: 1000,
  28. hideDelay: 1000,
  29. switchDelay: 1000,
  30. tipDuration: 2000,
  31. };
  32.  
  33. // Utility functions
  34. const util = {
  35. waitForGame: (callback) => {
  36. if (typeof network !== 'undefined' && typeof countryCode !== 'undefined') {
  37. callback();
  38. } else {
  39. setTimeout(() => util.waitForGame(callback), 100);
  40. }
  41. },
  42. getRegion: () => {
  43. if (CONFIG.euCountries.has(countryCode)) return 'Europe';
  44. if (CONFIG.asiaCountries.has(countryCode)) return 'Asia';
  45. return 'America';
  46. },
  47. createElement: (tag, options = {}) => {
  48. const element = document.createElement(tag);
  49. Object.assign(element, options);
  50. if (options.style) element.style.cssText = options.style;
  51. return element;
  52. },
  53. };
  54.  
  55. // Main class
  56. class ServerSwitcher {
  57. constructor() {
  58. this.elements = {};
  59. this.state = {
  60. isRegionSelectorOpen: false,
  61. isRoomCodeInputActive: false,
  62. };
  63. this.hideTimeout = null;
  64. }
  65.  
  66. init() {
  67. this.createUI();
  68. this.attachEventListeners();
  69. this.updateServerInfo();
  70. setInterval(() => this.updateServerInfo(), CONFIG.updateInterval);
  71. }
  72.  
  73. createUI() {
  74. this.elements = {
  75. container: util.createElement('div', {
  76. id: 'server-info-container',
  77. style: `
  78. position: absolute;
  79. top: 0px;
  80. right: 0px;
  81. display: flex;
  82. align-items: center;
  83. z-index: 1000;
  84. opacity: 0;
  85. `
  86. }),
  87. switcherContainer: util.createElement('div', {
  88. id: 'region-switcher-container',
  89. style: `
  90. position: relative;
  91. display: flex;
  92. align-items: center;
  93. margin-right: 10px;
  94. `
  95. }),
  96. switcherButton: util.createElement('button', {
  97. id: 'region-switcher',
  98. innerHTML: `
  99. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="30" height="30">
  100. <path fill="#00ffff" d="M0 96C0 43 43 0 96 0L384 0l32 0c17.7 0 32 14.3 32 32l0 320c0 17.7-14.3 32-32 32l0 64c17.7 0 32 14.3 32 32s-14.3 32-32 32l-32 0L96 512c-53 0-96-43-96-96L0 96zM64 416c0 17.7 14.3 32 32 32l256 0 0-64L96 384c-17.7 0-32 14.3-32 32zM247.4 283.8c-3.7 3.7-6.2 4.2-7.4 4.2s-3.7-.5-7.4-4.2c-3.8-3.7-8-10-11.8-18.9c-6.2-14.5-10.8-34.3-12.2-56.9l63 0c-1.5 22.6-6 42.4-12.2 56.9c-3.8 8.9-8 15.2-11.8 18.9zm42.7-9.9c7.3-18.3 12-41.1 13.4-65.9l31.1 0c-4.7 27.9-21.4 51.7-44.5 65.9zm0-163.8c23.2 14.2 39.9 38 44.5 65.9l-31.1 0c-1.4-24.7-6.1-47.5-13.4-65.9zM368 192a128 128 0 1 0 -256 0 128 128 0 1 0 256 0zM145.3 208l31.1 0c1.4 24.7 6.1 47.5 13.4 65.9c-23.2-14.2-39.9-38-44.5-65.9zm31.1-32l-31.1 0c4.7-27.9 21.4 51.7 44.5 65.9c-7.3 18.3-12 41.1-13.4 65.9zm56.1-75.8c3.7-3.7 6.2-4.2 7.4-4.2s3.7 .5 7.4 4.2c3.8 3.7 8 10 11.8 18.9c6.2 14.5 10.8 34.3 12.2 56.9l-63 0c1.5-22.6 6-42.4 12.2-56.9c3.8-8.9 8-15.2 11.8-18.9z"/>
  101. </svg>
  102. `,
  103. style: `
  104. background: none;
  105. border: none;
  106. cursor: pointer;
  107. padding: 5px;
  108. display: flex;
  109. align-items: center;
  110. `
  111. }),
  112. roomCodeInput: util.createElement('input', {
  113. type: 'text',
  114. placeholder: 'Enter room code',
  115. style: `
  116. position: absolute;
  117. top: 50%;
  118. right: 0;
  119. transform: translateY(-50%);
  120. width: 0;
  121. padding: 5px;
  122. border: #05ffff solid;
  123. border-width: 2px;
  124. border-radius: 5px;
  125. background-color: #003a3a;
  126. color: #05ffff;
  127. font-family: 'Arial', sans-serif;
  128. font-size: 14px;
  129. transition: width 0.3s ease-out, opacity 0.3s ease-out;
  130. opacity: 0;
  131. outline: none;
  132. `
  133. }),
  134. infoDiv: util.createElement('div', {
  135. id: 'server-info',
  136. style: `
  137. background-color: #003a3a;
  138. border-radius: 5px;
  139. padding: 3px 6px;
  140. border: #05ffff solid;
  141. border-width: 2px;
  142. border-radius: 0 0 0 5px;
  143. color: #05ffff;
  144. font-family: 'Arial Black', sans-serif;
  145. font-size: 14px;
  146. user-select: none;
  147. cursor: pointer;
  148. `
  149. }),
  150. regionOptions: []
  151. };
  152.  
  153. this.createRegionOptions();
  154.  
  155. this.elements.switcherContainer.append(this.elements.switcherButton, this.elements.roomCodeInput);
  156. this.elements.container.append(this.elements.switcherContainer, this.elements.infoDiv);
  157. document.body.appendChild(this.elements.container);
  158. }
  159.  
  160. createRegionOptions() {
  161. CONFIG.regions.forEach((region, index) => {
  162. const option = util.createElement('div', {
  163. textContent: region,
  164. style: `
  165. position: absolute;
  166. right: -70px;
  167. top: 50%;
  168. transform: translateY(-50%) translateX(0);
  169. padding: 5px;
  170. cursor: pointer;
  171. color: #05ffff;
  172. background: rgba(0, 58, 58, 0.7);
  173. backdrop-filter: blur(5px);
  174. border: 1px solid rgba(5, 255, 255, 0.3);
  175. border-radius: 5px;
  176. opacity: 0;
  177. transition: opacity 0.3s ease-out, transform 0.3s ease-out;
  178. z-index: ${1000 - index};
  179. width: 70px;
  180. text-align: center;
  181. `
  182. });
  183. option.addEventListener('click', (e) => {
  184. e.stopPropagation();
  185. this.switchRegion(index);
  186. });
  187. this.elements.switcherContainer.appendChild(option);
  188. this.elements.regionOptions.push(option);
  189. });
  190. }
  191.  
  192. attachEventListeners() {
  193. this.elements.infoDiv.addEventListener('click', () => this.copyRoomLink());
  194. this.elements.switcherContainer.addEventListener('mouseenter', () => this.handleContainerMouseEnter());
  195. this.elements.switcherContainer.addEventListener('mouseleave', () => this.handleContainerMouseLeave());
  196. this.elements.switcherButton.addEventListener('click', (e) => this.handleSwitcherButtonClick(e));
  197. this.elements.roomCodeInput.addEventListener('keydown', (e) => this.handleRoomCodeInputKeydown(e));
  198. document.addEventListener('click', (e) => this.handleDocumentClick(e));
  199. }
  200.  
  201. updateServerInfo() {
  202. const roomCode = network.roomID ? `#${network.roomID}` : 'Not in a room';
  203. const region = util.getRegion();
  204.  
  205. if (typeof localPlayer === 'undefined' || localPlayerID === 0) {
  206. this.elements.infoDiv.innerHTML = `Room: ${roomCode}<br>Region: ${region}`;
  207. this.elements.container.style.cssText += `
  208. transition: opacity ease-in 0.25s;
  209. transition-delay: 0.3s;
  210. opacity: 1;
  211. `;
  212. } else {
  213. this.elements.container.style.cssText += `
  214. transition: none;
  215. opacity: 0;
  216. `;
  217. }
  218. }
  219.  
  220. switchRegion(index) {
  221. const newRegion = CONFIG.regions[index];
  222. const newCC = CONFIG.countryCodes[newRegion];
  223.  
  224. countryCode = newCC;
  225. window.localStorage.wingsCC = newCC;
  226. window.localStorage.wingsCCTime = Date.now();
  227.  
  228. network.disconnect();
  229. setTimeout(() => network.getServerAndConnect(), CONFIG.switchDelay);
  230.  
  231. hud.showTip(`Switched to region: ${newRegion}`, CONFIG.tipDuration);
  232. }
  233.  
  234. toggleRegions() {
  235. this.state.isRegionSelectorOpen = !this.state.isRegionSelectorOpen;
  236. this.state.isRegionSelectorOpen ? this.showRegions() : this.hideRegions();
  237. }
  238.  
  239. showRegions() {
  240. clearTimeout(this.hideTimeout);
  241. this.elements.regionOptions.forEach((option, index) => {
  242. option.style.cssText += `
  243. opacity: 1;
  244. transform: translateY(-50%) translateX(${-120 - index * 90}px);
  245. `;
  246. });
  247. }
  248.  
  249. hideRegions() {
  250. this.elements.regionOptions.forEach((option) => {
  251. option.style.cssText += `
  252. opacity: 0;
  253. transform: translateY(-50%) translateX(-80%);
  254. `;
  255. });
  256. }
  257.  
  258. showRoomCodeInput() {
  259. this.state.isRoomCodeInputActive = true;
  260. this.elements.roomCodeInput.style.cssText += `
  261. width: 120px;
  262. opacity: 1;
  263. pointer-events: auto;
  264. `;
  265. this.elements.switcherButton.style.cssText += `
  266. opacity: 0;
  267. pointer-events: none;
  268. `;
  269. this.elements.roomCodeInput.focus();
  270. this.hideRegions();
  271. }
  272.  
  273. hideRoomCodeInput() {
  274. this.state.isRoomCodeInputActive = false;
  275. this.elements.roomCodeInput.style.cssText += `
  276. width: 0;
  277. opacity: 0;
  278. pointer-events: none;
  279. `;
  280. this.elements.switcherButton.style.cssText += `
  281. opacity: 1;
  282. pointer-events: auto;
  283. `;
  284. this.showRegions();
  285. }
  286.  
  287. connectToRoom(roomCode) {
  288. if (roomCode) {
  289. network.disconnect();
  290. setTimeout(() => {
  291. window.location.hash = roomCode;
  292. network.getServerAndConnect();
  293. }, CONFIG.switchDelay);
  294. hud.showTip(`Connecting to room: ${roomCode}`, CONFIG.tipDuration);
  295. this.updateServerInfo();
  296. }
  297. }
  298.  
  299. copyRoomLink() {
  300. const roomCode = network.roomID ? `#${network.roomID}` : '';
  301. const url = `http://powerline.io/${roomCode}`;
  302. navigator.clipboard.writeText(url)
  303. .then(() => console.log('Copied to clipboard:', url))
  304. .catch(err => console.error('Failed to copy to clipboard:', err));
  305. }
  306.  
  307. handleContainerMouseEnter() {
  308. clearTimeout(this.hideTimeout);
  309. if (!this.state.isRegionSelectorOpen && !this.state.isRoomCodeInputActive) {
  310. this.showRegions();
  311. }
  312. }
  313.  
  314. handleContainerMouseLeave() {
  315. if (!this.state.isRegionSelectorOpen && !this.state.isRoomCodeInputActive) {
  316. this.hideTimeout = setTimeout(() => this.hideRegions(), CONFIG.hideDelay);
  317. }
  318. }
  319.  
  320. handleSwitcherButtonClick(e) {
  321. e.stopPropagation();
  322. this.state.isRoomCodeInputActive ? this.hideRoomCodeInput() : this.showRoomCodeInput();
  323. }
  324.  
  325. handleRoomCodeInputKeydown(e) {
  326. if (e.key === 'Enter') {
  327. this.connectToRoom(this.elements.roomCodeInput.value);
  328. this.hideRoomCodeInput();
  329. } else if (e.key === 'Escape') {
  330. this.hideRoomCodeInput();
  331. }
  332. }
  333.  
  334. handleDocumentClick(e) {
  335. if (!this.elements.switcherContainer.contains(e.target)) {
  336. if (this.state.isRegionSelectorOpen) {
  337. this.toggleRegions();
  338. }
  339. if (this.state.isRoomCodeInputActive) {
  340. this.hideRoomCodeInput();
  341. }
  342. }
  343. }
  344. }
  345.  
  346. // Initialize the ServerSwitcher when the game is ready
  347. util.waitForGame(() => new ServerSwitcher().init());
  348. })();