Geoguessr Resolver Hack (Works in all game modes)

Features: Automatically score 5000 Points | Score randomly between 4500 and 5000 points | Open in Google Maps | See enemy guess Distance

当前为 2023-09-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Geoguessr Resolver Hack (Works in all game modes)
  3. // @namespace http://tampermonkey.net/
  4. // @version 10.2_Beta
  5. // @description Features: Automatically score 5000 Points | Score randomly between 4500 and 5000 points | Open in Google Maps | See enemy guess Distance
  6. // @author 0x978
  7. // @match https://www.geoguessr.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=geoguessr.com
  9. // @grant GM_webRequest
  10. // ==/UserScript==
  11.  
  12. window.alert = function (message) { // Devs tried to overwrite alert to detect script. I had already stopped using alert, but i'd rather they didn't override this anyway.
  13. nativeAlert(message)
  14. };
  15.  
  16. const originalFetch = window.fetch;
  17. window.fetch = function (url, options) {
  18. if (url === "https://www.geoguessr.com/api/v4/cd0d1298-a3aa-4bd0-be09-ccf513ad14b1") { // devs using this endpoint for Anticheat. Block all calls to it.
  19. return
  20. }
  21. return originalFetch.call(this, url, options);
  22. };
  23.  
  24. async function getAddress(lat, lon) {
  25. const response = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=json`)
  26. return await response.json();
  27. }
  28.  
  29. function displayLocationInfo() {
  30. const coordinates = coordinateClimber()
  31. // api call with the lat lon to retrieve data.
  32. getAddress(coordinates[0], coordinates[1]).then(data => {
  33. alert(`
  34. Country: ${data.address.country}
  35. County: ${data.address.county}
  36. City: ${data.address.city}
  37. Road: ${data.address.road}
  38. State: ${data.address.state}
  39. Postcode: ${data.address.postcode}
  40. Village/Suburb: ${(data.address.village || data.address.suburb)}
  41.  
  42. Postal Address: ${data.display_name}
  43. `)
  44. });
  45.  
  46. }
  47.  
  48. function placeMarker(safeMode, skipGet, coords) {
  49. const isPanic = document.getElementsByClassName("coordinate-map_canvasContainer__7d8Yw")[0]
  50. if(isPanic){panicPlaceMarker(isPanic); return;}
  51. const isStreaks = document.getElementsByClassName("guess-map__canvas-container")[0] === undefined
  52. let location = skipGet ? coords : coordinateClimber(isStreaks)
  53. if (isStreaks) {
  54. placeMarkerStreaksMode(location)
  55. return;
  56. }
  57. let [lat, lng] = location
  58.  
  59. if (safeMode) {
  60. const sway = [Math.random() > 0.5,Math.random() > 0.5]
  61. const multiplier = Math.random() * 4
  62. const horizontalAmount = Math.random() * multiplier
  63. const verticalAmount = Math.random() * multiplier
  64. sway[0] ? lat += verticalAmount : lat -= verticalAmount
  65. sway[1] ? lng += horizontalAmount : lat -= horizontalAmount
  66. }
  67.  
  68. const element = document.getElementsByClassName("guess-map__canvas-container")[0] // html element containing needed props.
  69. const keys = Object.keys(element) // all keys
  70. const key = keys.find(key => key.startsWith("__reactFiber$")) // the React key I need to access props
  71. const place = element[key].return.memoizedProps.onMarkerLocationChanged // getting the function which will allow me to place a marker on the map
  72.  
  73. flag = false;
  74. place({lat: lat, lng: lng}) // placing the marker on the map at the correct coordinates given by getCoordinates(). Must be passed as an Object.
  75. toggleClick(({lat: lat, lng: lng}))
  76. displayDistanceFromCorrect({lat: lat, lng: lng})
  77. injectOverride()
  78. }
  79.  
  80. function placeMarkerStreaksMode(code) {
  81. let element = document.getElementsByClassName("region-map_map__5e4h8")[0] // this map is unique to streaks mode, however, is similar to that found in normal modes.
  82. if(!element){
  83. element = document.getElementsByClassName("region-map_map__7jxcD")[0]
  84. }
  85. const keys = Object.keys(element)
  86. const reactKey = keys.find(key => key.startsWith("__reactFiber$"))
  87. const placeMarkerFunction = element[reactKey].return.memoizedProps.onRegionSelected // This map works by selecting regions, not exact coordinates on a map, which is handles by the "onRegionSelected" function.
  88.  
  89. if(typeof code !== "string"){
  90. let [lat,lng] = code
  91. getAddress(lat, lng).then(data => { // using API to retrieve the country code at the current coordinates.
  92. const countryCode = data.address.country_code
  93. placeMarkerFunction(countryCode) // passing the country code into the onRegionSelected method.
  94. })
  95. return
  96. }
  97.  
  98. placeMarkerFunction(code)
  99. }
  100.  
  101. function panicPlaceMarker(element){ // Currently only used in map runner.
  102. const keys = Object.keys(element)
  103. const key = keys.find(key => key.startsWith("__reactFiber$"))
  104. const props = element[key]
  105.  
  106. const clickProperty = props.return.memoizedProps.map.__e3_.click // taking the maps click property directly.
  107. const clickFunction = clickProperty[getDynamicIndex(Object.keys(clickProperty),clickProperty)].xe
  108. console.log(clickFunction)
  109. let [lat,lng] = coordinateClimber()
  110.  
  111. // for some reason, submitting near-perfect guesses causes Chromium browsers to crash, the following will offset it to avoid this.
  112. // There is probably some other way to avoid this, but I have no idea why this happens. See GitHub issue.
  113. lat += 0.1
  114. lng += 0.1
  115.  
  116. let y = {
  117. "latLng": {
  118. "lat": () => lat,
  119. "lng": () => lng,
  120. }
  121. }
  122. clickFunction(y)
  123. }
  124.  
  125. function getDynamicIndex(indexArray,clickProperty){
  126. for(let i = 0; i < indexArray.length;i++){
  127. if(clickProperty[indexArray[i]]?.xe.toString().slice(0,20) === "l=>{let e={lat:l.lat"){
  128. return indexArray[i]
  129. }
  130. }
  131. alert("Maprunner Placer failed. \n Please report this on GitHub or Greasyfork.")
  132. }
  133.  
  134. function coordinateClimber(isStreaks){
  135. let timeout = 10
  136. let path = document.querySelector('div[data-qa="panorama"]');
  137. while (timeout > 0){
  138. const props = path[Object.keys(path).find(key => key.startsWith("__reactFiber$"))]
  139. const checkReturns = iterateReturns(props,isStreaks)
  140. if(checkReturns){
  141. return checkReturns
  142. }
  143. path = path.parentNode
  144. timeout--
  145. }
  146. alert("Failed to find co-ordinates. Please make an issue on GitHub or GreasyFork. " +
  147. "Please make sure you mention the game mode in your report.")
  148. }
  149.  
  150. function iterateReturns(element,isStreaks){
  151. let timeout = 10
  152. let path = element
  153. while(timeout > 0){
  154. const coords = checkProps(path.memoizedProps,isStreaks)
  155. if(coords){
  156. return coords
  157. }
  158. path = element.return
  159. timeout--
  160. }
  161. }
  162.  
  163. function checkProps(props,isStreaks){
  164. if(props?.panoramaRef){
  165. const found = props.panoramaRef.current.location.latLng
  166. return [found.lat(),found.lng()]
  167. }
  168. if(props.streakLocationCode && isStreaks){
  169. return props.streakLocationCode
  170. }
  171. if(props.lat){
  172. return [props.lat,props.lng]
  173. }
  174. }
  175.  
  176. function mapsFromCoords() { // opens new Google Maps location using coords.
  177. const [lat, lon] = coordinateClimber()
  178. if (!lat || !lon) {
  179. return;
  180. }
  181. window.open(`https://www.google.com/maps/place/${lat},${lon}`);
  182. }
  183.  
  184. function getGuessDistance(manual) {
  185. const coords = coordinateClimber()
  186. const clat = coords[0] * (Math.PI / 180)
  187. const clng = coords[1] * (Math.PI / 180)
  188. const y = document.getElementsByClassName("guess-map__canvas-container")[0]
  189. const keys = Object.keys(y)
  190. const key = keys.find(key => key.startsWith("__reactFiber$"))
  191. const props = y[key]
  192. const user = manual ?? props.return.memoizedProps.markers[0]
  193. if (!coords || !user) {
  194. return null
  195. }
  196. const ulat = user.lat * (Math.PI / 180)
  197. const ulng = user.lng * (Math.PI / 180)
  198.  
  199. const distance = Math.acos(Math.sin(clat) * Math.sin(ulat) + Math.cos(clat) * Math.cos(ulat) * Math.cos(ulng - clng)) * 6371
  200. return distance
  201. }
  202.  
  203. function displayDistanceFromCorrect(manual) {
  204. let unRoundedDistance = getGuessDistance(manual) // need unrounded distance for precise calculations later.
  205. let distance = Math.round(unRoundedDistance)
  206. if (distance === null) {
  207. return
  208. }
  209. let text = `${distance} km (${Math.round(distance * 0.621371)} miles)`
  210. setGuessButtonText(text)
  211. }
  212.  
  213. function setGuessButtonText(text) {
  214. let x = document.querySelector('button[data-qa="perform-guess"]');
  215. if(!x){
  216. return null}
  217. x.innerText = text
  218. }
  219.  
  220. function toggleClick(coords) { // prevents user from making 5k guess to prevent bans.
  221. const disableSpaceBar = (e) => {
  222. if (e.keyCode === 32) {
  223. const distance = getGuessDistance()
  224. if ((distance < 1 || isNaN(distance)) && !flag) {
  225. e.stopImmediatePropagation();
  226. preventedActionPopup()
  227. document.removeEventListener("keyup", disableSpaceBar);
  228. flag = true
  229. }
  230. }
  231. };
  232. document.addEventListener("keyup", disableSpaceBar);
  233. setTimeout(() => {
  234. const distance = getGuessDistance()
  235. if ((distance < 1 || isNaN(distance)) && !flag) {
  236. let old = document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0][Object.keys(document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0])[1]].onClick
  237. document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0][Object.keys(document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0])[1]].onClick = (() => {
  238. flag = true
  239. preventedActionPopup()
  240. document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0][Object.keys(document.getElementsByClassName("button_button__CnARx button_variantPrimary__xc8Hp")[0])[1]].onClick = (() => old())
  241. })
  242. }
  243. }, 500)
  244. }
  245.  
  246. function preventedActionPopup() {
  247. alert(`Geoguessr Resolver has prevented you from making a perfect guess.
  248.  
  249. Making perfect guesses will very likely result in a ban from competitive.
  250.  
  251. Press "guess" again to proceed anyway.`)
  252. }
  253.  
  254. function injectOverride() {
  255. document.getElementsByClassName("guess-map__canvas-container")[0].onpointermove = (() => { // this is called wayyyyy too many times (thousands) but fixes a lot of issues over using onClick.
  256. displayDistanceFromCorrect()
  257. })
  258. }
  259.  
  260. function getBRCoordinateGuesses() {
  261. const gameRoot = document.getElementsByClassName("game_root__2vV1H")[0]
  262. const props = gameRoot[Object.keys(document.getElementsByClassName("game_root__2vV1H")[0])[0]]
  263. const gameProps = props.return.return.memoizedProps.value.gameState
  264. const roundNumber = gameProps.currentRoundNumber
  265. const playerArray = gameProps.players
  266.  
  267. let bestGuessDistance = Number.MAX_SAFE_INTEGER
  268.  
  269. playerArray.forEach((player) => {
  270. const guesses = player.coordinateGuesses
  271. if(guesses){
  272. const guess = guesses[guesses.length - 1]
  273. if(guess && guess.roundNumber === roundNumber){
  274. if(guess.distance < bestGuessDistance){
  275. bestGuessDistance = guess.distance
  276. }
  277. }
  278. }
  279. })
  280.  
  281. if (bestGuessDistance === Number.MAX_SAFE_INTEGER) {
  282. return null;
  283. }
  284. return Math.round(bestGuessDistance / 1000)
  285. }
  286.  
  287. function displayBRGuesses(){
  288. const distance = getBRCoordinateGuesses()
  289. if (distance === null) {
  290. alert("There have been no guesses this round")
  291. return;
  292. }
  293. alert(`The best guess this round is ${distance} km from the correct location. (This may include your guess)`)
  294. }
  295.  
  296. function setInnerText(){
  297. const text = `
  298. Geoguessr Resolver Loaded Successfully
  299.  
  300. IMPORTANT GEOGUESSR RESOLVER UPDATE INFORMATION: https://text.0x978.com/geoGuessr
  301. MAPRUNNER IS NOW SUPPORTED (BETA). Please try it out and report issues on GitHub
  302. Please note the MapRunner guesser is not 100% accurate, and usually scores around 4800 to 4900 points
  303. `
  304. if(document.getElementsByClassName("header_logo__vV0HK")[0]){
  305. document.getElementsByClassName("header_logo__vV0HK")[0].innerText = text
  306. }
  307. }
  308.  
  309. GM_webRequest([
  310. { selector: 'https://www.geoguessr.com/api/v4/trails', action: 'cancel' },
  311. ]);
  312.  
  313. let onKeyDown = (e) => {
  314. if (e.keyCode === 49) {
  315. e.stopImmediatePropagation(); // tries to prevent the key from being hijacked by geoguessr
  316. placeMarker(true, false, undefined)
  317. }
  318. if (e.keyCode === 50) {
  319. e.stopImmediatePropagation();
  320. placeMarker(false, false, undefined)
  321. }
  322. if (e.keyCode === 51) {
  323. e.stopImmediatePropagation();
  324. displayLocationInfo()
  325. }
  326. if (e.keyCode === 52) {
  327. e.stopImmediatePropagation();
  328. mapsFromCoords()
  329. }
  330. if (e.keyCode === 53) {
  331. e.stopImmediatePropagation();
  332. displayBRGuesses()
  333. }
  334. }
  335. setInnerText()
  336. document.addEventListener("keydown", onKeyDown);
  337. let flag = false