Greasy Fork镜像 支持简体中文。

DigDig.IO Server Selector

Server selector for digdig.io. Double click to copy, single click to download.

目前為 2021-07-18 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name DigDig.IO Server Selector
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2.3
  5. // @description Server selector for digdig.io. Double click to copy, single click to download.
  6. // @author Zertalious (Zert)
  7. // @match *://digdig.io/*
  8. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  9. // @grant unsafeWindow
  10. // @grant GM_addStyle
  11. // ==/UserScript==
  12.  
  13. const gameSockets = [];
  14.  
  15. let lastServerGameTriedConnectingTo;
  16.  
  17. unsafeWindow.console.log = new Proxy( unsafeWindow.console.log, {
  18. apply( target, thisArgs, args ) {
  19.  
  20. const x = args[ 0 ];
  21.  
  22. if ( ! lastServerGameTriedConnectingTo && typeof x === 'string' && x.startsWith( 'Connecting to ' ) ) {
  23.  
  24. lastServerGameTriedConnectingTo = x.slice( 0, x.indexOf( '.' ) ).split( ' ' )[ 2 ];
  25.  
  26. }
  27.  
  28. return Reflect.apply( ...arguments );
  29.  
  30. }
  31. } );
  32.  
  33. unsafeWindow.WebSocket = new Proxy( unsafeWindow.WebSocket, {
  34. construct( target, args ) {
  35.  
  36. const isGameSocket = args[ 0 ].endsWith( ':443' );
  37.  
  38. if ( isGameSocket && selectedServerId ) {
  39.  
  40. const url = new URL( args[ 0 ] );
  41.  
  42. const items = url.hostname.split( '.' );
  43.  
  44. if ( items[ 0 ] !== selectedServerId ) {
  45.  
  46. if ( lastServerGameTriedConnectingTo === items[ 0 ] ) {
  47.  
  48. console.log( '[SERVER SELECTOR] Overriding...', { from: items[ 0 ], to: selectedServerId } );
  49.  
  50. items[ 0 ] = selectedServerId;
  51.  
  52. url.hostname = items.join( '.' );
  53.  
  54. args[ 0 ] = url.toString();
  55.  
  56. } else {
  57.  
  58. console.log( '[SERVER SELETOR] Internal server switch! Updating selected server...', { from: selectedServerId, to: items[ 0 ] } );
  59.  
  60. lastServerGameTriedConnectingTo = items[ 0 ];
  61.  
  62. setSelectedServerId( items[ 0 ] );
  63.  
  64. }
  65.  
  66. }
  67.  
  68. }
  69.  
  70. const socket = Reflect.construct( ...arguments );
  71.  
  72. if ( isGameSocket ) {
  73.  
  74. gameSockets.push( socket );
  75.  
  76. }
  77.  
  78. return socket;
  79.  
  80. }
  81. } );
  82.  
  83. function reconnect() {
  84.  
  85. while ( gameSockets.length > 0 ) {
  86.  
  87. try {
  88.  
  89. gameSockets.shift().close();
  90.  
  91. } catch ( err ) {}
  92.  
  93. }
  94.  
  95. }
  96.  
  97. GM_addStyle( `
  98.  
  99. body {
  100. margin: 0;
  101. overflow: hidden;
  102. background: #744100;
  103. font-family: 'Ubuntu';
  104. text-shadow: 1px 0 #000, -1px 0 #000, 0 1px #000, 0 -1px #000, 1px 1px #000, -1px -1px #000;
  105. }
  106.  
  107. .group {
  108. position: absolute;
  109. right: 15px;
  110. bottom: 15px;
  111. display: flex;
  112. flex-direction: column;
  113. }
  114.  
  115. .group > * {
  116. margin-bottom: 8px;
  117. }
  118.  
  119. .group > *:last-child {
  120. margin-bottom: 0;
  121. }
  122.  
  123. .btn {
  124. color: #fff;
  125. background: #aeaeae;
  126. font-size: 2.25em;
  127. text-align: center;
  128. padding: 0.2em;
  129. cursor: pointer;
  130. box-shadow: inset 0 0 0 0.1em rgba(0, 0, 0, 0.25);
  131. border-radius: 0.1em;
  132. position: relative;
  133. user-select: none;
  134. }
  135.  
  136. .btn:before {
  137. content: ' ';
  138. position: absolute;
  139. top: 0.1em;
  140. left: 0.1em;
  141. width: calc(100% - 0.2em);
  142. height: calc(100% - 0.2em);
  143. background: transparent;
  144. }
  145.  
  146. .btn:hover:before {
  147. background: hsla(0, 0%, 100%, 0.25);
  148. }
  149.  
  150. .btn:active:before {
  151. background: rgba(0, 0, 0, 0.1);
  152. }
  153.  
  154. .btn i {
  155. text-shadow: none;
  156. }
  157.  
  158. [tooltip] {
  159. position: relative;
  160. }
  161.  
  162. [tooltip]:after {
  163. content: attr(tooltip);
  164. font-size: 0.9rem;
  165. position: absolute;
  166. right: 100%;
  167. top: 50%;
  168. transform: translate(-10px, -50%);
  169. white-space: nowrap;
  170. pointer-events: none;
  171. background: rgba(0, 0, 0, 0.5);
  172. padding: 0.25em 0.4em;
  173. border-radius: 0.2em;
  174. opacity: 0;
  175. transition: 0.2s;
  176. }
  177.  
  178. [tooltip]:not(.disabled):hover:after {
  179. opacity: 1;
  180. }
  181.  
  182. [tooltip]:is(.force-tooltip):after {
  183. opacity: 1 !important;
  184. }
  185.  
  186. .dialog {
  187. position: absolute;
  188. right: 85px;
  189. bottom: 15px;
  190. color: #fff;
  191. background: #aeaeae;
  192. padding: 0.75em;
  193. border-radius: 0.3em;
  194. box-shadow: inset 0 0 0 0.3em rgba(0, 0, 0, 0.25);
  195. width: 300px;
  196. transition: 0.2s;
  197. }
  198.  
  199. .dialog > * {
  200. margin-bottom: 5px;
  201. }
  202.  
  203. .dialog > *:last-child {
  204. margin-bottom: 0;
  205. }
  206.  
  207. .dialog.disabled {
  208. transform: translate(0, calc(100% + 15px));
  209. }
  210.  
  211. .dialog .btn {
  212. font-size: 1.25rem;
  213. background: #bb5555;
  214. }
  215.  
  216. .title {
  217. font-size: 1.5em;
  218. text-align: center;
  219. }
  220.  
  221. .spinner {
  222. margin: 10px auto;
  223. width: 60px;
  224. height: 60px;
  225. border: 10px solid transparent;
  226. border-top-color: rgba(0, 0, 0, 0.3);
  227. border-radius: 50%;
  228. animation: spin 0.5s infinite;
  229. }
  230.  
  231. .option {
  232. background: rgba(0, 0, 0, 0.1);
  233. padding: 0.5em 0.75em;
  234. border-radius: 0.25em;
  235. cursor: pointer;
  236. }
  237.  
  238. .option.active {
  239. box-shadow: inset 0 0 0 0.15em rgba(0, 0, 0, 0.2);
  240. }
  241.  
  242. @keyframes spin {
  243. from {
  244. transform: rotate(0);
  245. }
  246.  
  247. to {
  248. transform: rotate(360deg);
  249. }
  250. }
  251.  
  252. ` );
  253.  
  254. const temp = document.createElement( 'div' );
  255.  
  256. temp.innerHTML += `
  257.  
  258. <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"/>
  259. <div class="group">
  260. <div class="btn leaderboard-btn" tooltip="Capture leaderboard [G]">
  261. <i class="fa fa-trophy"></i>
  262. </div>
  263. <div class="btn screenshot-btn" tooltip="Take screenshot [F]">
  264. <i class="fa fa-camera"></i>
  265. </div>
  266. <div class="btn servers-btn" tooltip="Servers">
  267. <i class="fa fa-globe"></i>
  268. </div>
  269. </div>
  270. <div class="dialog disabled">
  271. <div class="title">Servers</div>
  272. <div class="spinner"></div>
  273. <div class="btn close-btn">close</div>
  274. <div class="btn refresh-btn">refresh</div>
  275. </div>
  276. </div>
  277.  
  278. `;
  279.  
  280. while ( temp.children.length > 0 ) {
  281.  
  282. document.body.appendChild( temp.children[ 0 ] );
  283.  
  284. }
  285.  
  286. temp.innerHTML = '';
  287.  
  288. const spinner = document.querySelector( '.spinner' );
  289.  
  290. ( async function () {
  291.  
  292. await fetchServers( 'ffa' );
  293. await fetchServers( 'teams' );
  294.  
  295. spinner.style.display = 'none';
  296.  
  297. } )();
  298.  
  299. const serverIds = {};
  300.  
  301. let selectedServerId = new URLSearchParams( document.location.search.substring( 1 ) ).get( 'server' );
  302.  
  303. if ( selectedServerId ) {
  304.  
  305. reconnect();
  306.  
  307. }
  308.  
  309. async function fetchServers( mode ) {
  310.  
  311. const response = await fetch( 'https://api.n.m28.io/endpoint/digdig-' + mode + '/findEach' );
  312. const json = await response.json();
  313.  
  314. for ( let key in json.servers ) {
  315.  
  316. const id = json.servers[ key ].id;
  317.  
  318. if ( ! serverIds[ id ] ) {
  319.  
  320. serverIds[ id ] = mode;
  321.  
  322. const div = document.createElement( 'div' );
  323. div.classList.add( 'option' );
  324. div.innerHTML = mode + '_' + key.split( '-' )[ 1 ] + '_' + id;
  325. div.setAttribute( 'data-id', id );
  326.  
  327. dialog.appendChild( div );
  328.  
  329. div.onclick = function () {
  330.  
  331. setSelectedServerId( id );
  332.  
  333. reconnect();
  334.  
  335. }
  336.  
  337. if ( ! selectedServerId ) {
  338.  
  339. div.click();
  340.  
  341. } else if ( selectedServerId === id ) {
  342.  
  343. div.click();
  344.  
  345. }
  346.  
  347. }
  348.  
  349. }
  350.  
  351. }
  352.  
  353. function setSelectedServerId( id ) {
  354.  
  355. selectedServerId = id;
  356.  
  357. const url = new URL( window.location );
  358. url.searchParams.set( 'server', selectedServerId );
  359.  
  360. history.pushState( {}, document.title, url );
  361.  
  362. const active = document.querySelector( '.option.active' );
  363.  
  364. if ( active ) {
  365.  
  366. active.classList.remove( 'active' );
  367.  
  368. }
  369.  
  370. const option = document.querySelector( '[data-id="' + id + '"]' );
  371.  
  372. if ( option ) {
  373.  
  374. option.classList.add( 'active' );
  375.  
  376. }
  377.  
  378. }
  379.  
  380. const dialog = document.querySelector( '.dialog' );
  381. const serversBtn = document.querySelector( '.servers-btn' );
  382.  
  383. serversBtn.onclick = function () {
  384.  
  385. if ( dialog.classList.contains( 'disabled' ) ) {
  386.  
  387. dialog.classList.remove( 'disabled' );
  388. this.classList.add( 'disabled' );
  389.  
  390. } else {
  391.  
  392. hideDialog();
  393.  
  394. }
  395.  
  396. }
  397.  
  398. document.getElementById( 'canvas' ).onclick = hideDialog;
  399. document.querySelector( '.close-btn' ).onclick = hideDialog;
  400.  
  401. document.querySelector( '.refresh-btn' ).onclick = async function () {
  402.  
  403. spinner.style.display = '';
  404.  
  405. await fetchServers( 'ffa' );
  406. await fetchServers( 'teams' );
  407.  
  408. spinner.style.display = 'none';
  409.  
  410. };
  411.  
  412. function hideDialog() {
  413.  
  414. dialog.classList.add( 'disabled' );
  415. serversBtn.classList.remove( 'disabled' );
  416.  
  417. }
  418.  
  419. function createClickListener( a, b ) {
  420.  
  421. let clicks = 0;
  422.  
  423. return function () {
  424.  
  425. clicks ++;
  426.  
  427. setTimeout( function () {
  428.  
  429. if ( clicks === 1 ) {
  430.  
  431. a();
  432.  
  433. }
  434.  
  435. clicks = 0;
  436.  
  437. }, 300 );
  438.  
  439. if ( clicks === 2 ) {
  440.  
  441. b();
  442.  
  443. }
  444.  
  445. }
  446.  
  447. }
  448.  
  449. const screenshotBtn = document.querySelector( '.screenshot-btn' );
  450. const leaderboardBtn = document.querySelector( '.leaderboard-btn' );
  451.  
  452. const displayCopiedScreenshot = createCopiedDisplayer( screenshotBtn );
  453. const displayCopiedLeaderboard = createCopiedDisplayer( leaderboardBtn );
  454.  
  455. screenshotBtn.onclick = createClickListener(
  456. function () {
  457.  
  458. downloadCanvas( document.querySelector( 'canvas' ), 'digdig' );
  459.  
  460. },
  461. copyScreenshot
  462. );
  463.  
  464. leaderboardBtn.onclick = createClickListener(
  465. function () {
  466.  
  467. if ( leaderboard ) {
  468.  
  469. downloadCanvas( getLeaderboardCanvas(), 'digdig_leaderboard' );
  470.  
  471. }
  472.  
  473. },
  474. copyLeaderboard
  475. );
  476.  
  477. function copyScreenshot() {
  478.  
  479. copyCanvasToClipboard( document.querySelector( 'canvas' ) );
  480.  
  481. displayCopiedScreenshot();
  482.  
  483. }
  484.  
  485. function copyLeaderboard() {
  486.  
  487. if ( leaderboard ) {
  488.  
  489. copyCanvasToClipboard( getLeaderboardCanvas() );
  490.  
  491. displayCopiedLeaderboard();
  492.  
  493. }
  494.  
  495. }
  496.  
  497. window.addEventListener( 'keyup', function ( event ) {
  498.  
  499. const key = String.fromCharCode( event.keyCode );
  500.  
  501. if ( key === 'F' ) {
  502.  
  503. copyScreenshot();
  504.  
  505. } else if ( key === 'G' ) {
  506.  
  507. copyLeaderboard();
  508.  
  509. }
  510.  
  511. } );
  512.  
  513. function createCopiedDisplayer( element ) {
  514.  
  515. let old, timeout;
  516.  
  517. return function () {
  518.  
  519. if ( element.classList.contains( 'force-tooltip' ) ) {
  520.  
  521. clearTimeout( timeout );
  522.  
  523. } else {
  524.  
  525. old = element.getAttribute( 'tooltip' );
  526. element.setAttribute( 'tooltip', 'Copied!' );
  527. element.classList.add( 'force-tooltip' );
  528.  
  529. }
  530.  
  531. timeout = setTimeout( function () {
  532.  
  533. element.setAttribute( 'tooltip', old );
  534. element.classList.remove( 'force-tooltip' );
  535.  
  536. }, 1000 );
  537.  
  538. }
  539.  
  540. }
  541.  
  542. function getLeaderboardCanvas() {
  543.  
  544. const offset = - 115;
  545.  
  546. const canvas = document.createElement( 'canvas' );
  547. canvas.width = leaderboard.width;
  548. canvas.height = leaderboard.height + offset;
  549.  
  550. canvas.getContext( '2d' ).drawImage( leaderboard, 0, offset );
  551.  
  552. return canvas;
  553.  
  554. }
  555.  
  556. function copyCanvasToClipboard( canvas ) {
  557.  
  558. canvas.toBlob( function ( blob ) {
  559.  
  560. navigator.clipboard.write( [ new ClipboardItem( { 'image/png': blob } ) ] );
  561.  
  562. } );
  563.  
  564. }
  565.  
  566. function downloadCanvas( canvas, filename ) {
  567.  
  568. const a = document.createElement( 'a' );
  569.  
  570. a.href = canvas.toDataURL();
  571. a.download = filename + '_' + Date.now() + '.png';
  572.  
  573. a.click();
  574.  
  575. }
  576.  
  577. let leaderboard;
  578. let leaderboardPartial;
  579.  
  580. const Canvas = unsafeWindow.OffscreenCanvas ? unsafeWindow.OffscreenCanvas.prototype : unsafeWindow.HTMLCanvasElement.prototype;
  581.  
  582. Canvas.getContext = new Proxy( Canvas.getContext, {
  583. apply() {
  584.  
  585. const ctx = Reflect.apply( ...arguments );
  586.  
  587. ctx.fillText = new Proxy( ctx.fillText, {
  588. apply( target, thisArgs, args ) {
  589.  
  590. if ( args[ 0 ].indexOf( 'Diggers' ) > - 1 ) {
  591.  
  592. leaderboardPartial = ctx.canvas;
  593.  
  594. }
  595.  
  596. return Reflect.apply( ...arguments );
  597.  
  598. }
  599. } );
  600.  
  601. ctx.drawImage = new Proxy( ctx.drawImage, {
  602. apply( target, thisArgs, args ) {
  603.  
  604. if ( args[ 0 ] === leaderboardPartial ) {
  605.  
  606. leaderboard = ctx.canvas;
  607.  
  608. }
  609.  
  610. return Reflect.apply( ...arguments );
  611.  
  612. }
  613. } );
  614.  
  615. return ctx;
  616.  
  617. }
  618. } )

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址