DigDig.IO Server Selector

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

目前为 2021-08-03 提交的版本。查看 最新版本

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

QingJ © 2025

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