commit c950db4dfe28b2073a06f7cdfb717fdb225a36c2
parent 178f2f4ac6b2065147fa415da2093e41101c806a
Author: w451 <115924828+w451@users.noreply.github.com>
Date: Sat, 27 Apr 2024 08:00:20 -0400
feat: Random walking around in spoofed location (#805)
Co-authored-by: w451 <cheb>
Co-authored-by: auth <64337177+authorisation@users.noreply.github.com>
Diffstat:
5 files changed, 81 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
@@ -249,6 +249,7 @@ Thanks to everyone involved including the [third-party libraries](https://github
- [TheVisual](https://github.com/TheVisual)
- [CanerKaraca23](https://github.com/CanerKaraca23)
- [bocajthomas](https://github.com/bocajthomas)
+- [w451](https://github.com/w451)
## Donate
- LTC: LbBnT9GxgnFhwy891EdDKqGmpn7XtduBdE
diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json
@@ -636,6 +636,10 @@
"name": "Coordinates",
"description": "Set the coordinates of the spoofed location"
},
+ "walk_radius": {
+ "name": "Walk Radius",
+ "description": "Randomly walk around within this radius (ft)"
+ },
"always_update_location": {
"name": "Always Update Location",
"description": "Force Snapchat to update location even if no GPS data is received"
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Global.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Global.kt
@@ -21,6 +21,7 @@ class Global : ConfigContainer() {
inner class BetterLocation : ConfigContainer(hasGlobalState = true) {
val spoofLocation = boolean("spoof_location") { requireRestart() }
val coordinates = mapCoordinates("coordinates", 0.0 to 0.0) // lat, long
+ val walkRadius = string("walk_radius") { requireRestart(); inputCheck = { it.toDoubleOrNull()?.isFinite() == true && it.toDouble() >= 0.0 } }
val alwaysUpdateLocation = boolean("always_update_location") { requireRestart() }
val suspendLocationUpdates = boolean("suspend_location_updates") { requireRestart() }
val spoofBatteryLevel = string("spoof_battery_level") { requireRestart(); inputCheck = { it.isEmpty() || it.toIntOrNull() in 0..100 } }
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/BetterLocation.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/BetterLocation.kt
@@ -11,9 +11,78 @@ import me.rhunk.snapenhance.core.features.impl.global.SuspendLocationUpdates
import me.rhunk.snapenhance.core.util.hook.HookStage
import me.rhunk.snapenhance.core.util.hook.hook
import java.nio.ByteBuffer
+import kotlin.math.cos
+import kotlin.math.hypot
+import kotlin.math.pow
+import kotlin.math.sin
+import kotlin.math.sqrt
import kotlin.time.Duration.Companion.days
class BetterLocation : Feature("Better Location", loadParams = FeatureLoadParams.INIT_SYNC) {
+ //Latitude ft / deg /Longitude ft /deg = 1.26301179736
+ // 4ft/s * 1 degree/364000ft (latitude) * 1s/1000ms = .000000010989011 degrees/ms
+ val max_speed = 4.0 / 364000.0 / 1000.0
+ val pause_chance = .0023 // .23% chance to pause every second = after 5 minutes 50% chance of pause
+ val pause_duration = 60000L //ms
+ val pause_random = 30000L //ms
+
+ var pause_expire = 0L
+ var current_x = 0.0
+ var current_y = 0.0
+ var target_x = 0.0
+ var target_y = 0.0
+ var last_update_time = 0L
+ private fun updatePosition(){
+ val now = System.currentTimeMillis()
+
+ if(current_x == target_x && current_y == target_y) {
+ val config = context.config.global.betterLocation
+ val walk_rad = if (config.walkRadius.get()
+ .toDoubleOrNull() == null
+ ) 0.0 else (config.walkRadius.get().toDouble() / 364000.0) //Lat deg
+
+ if(last_update_time == 0L){ //Start at random position
+ val radius1 = sqrt(Math.random()) * walk_rad
+ val theta1 = Math.PI * 2.0 * Math.random()
+ current_x = cos(theta1) * radius1 * 1.26301179736
+ current_y = sin(theta1) * radius1
+ }
+
+ val radius2 = sqrt(Math.random()) * walk_rad
+ val theta2 = Math.PI * 2.0 * Math.random()
+ target_x = cos(theta2) * radius2 * 1.26301179736
+ target_y = sin(theta2) * radius2
+ } else if (pause_expire < now) {
+ val deltat = now - last_update_time
+ if(Math.random() > (1.0 - pause_chance).pow(deltat / 1000.0)){
+ pause_expire = now + pause_duration + (pause_random * Math.random()).toLong()
+ } else {
+ val max_dist = max_speed * deltat
+ val dist = hypot(target_x - current_x, target_y - current_y)
+
+ if (dist <= max_dist) {
+ current_x = target_x
+ current_y = target_y
+ } else {
+ val norm_x = (target_x - current_x) / dist * max_dist
+ val norm_y = (target_y - current_y) / dist * max_dist
+ current_x += norm_x
+ current_y += norm_y
+ }
+ }
+ }
+ last_update_time = now
+ }
+
+ private fun getLat() : Double {
+ updatePosition()
+ return (context.config.global.betterLocation.coordinates.get().first + current_x)
+ }
+
+ private fun getLong() : Double {
+ updatePosition()
+ return (context.config.global.betterLocation.coordinates.get().second + current_y)
+ }
private fun editClientUpdate(editor: EditorContext) {
val config = context.config.global.betterLocation
@@ -22,11 +91,10 @@ class BetterLocation : Feature("Better Location", loadParams = FeatureLoadParams
edit(1) {
context.log.verbose("SCVSLocationUpdate ${this@apply}")
if (config.spoofLocation.get()) {
- val coordinates by config.coordinates
remove(1)
remove(2)
- addFixed32(1, coordinates.first.toFloat()) // lat
- addFixed32(2, coordinates.second.toFloat()) // lng
+ addFixed32(1, getLat().toFloat()) // lat
+ addFixed32(2, getLong().toFloat()) // lng
}
if (config.alwaysUpdateLocation.get()) {
@@ -79,9 +147,9 @@ class BetterLocation : Feature("Better Location", loadParams = FeatureLoadParams
}
Location::class.java.apply {
hook("getLatitude", HookStage.BEFORE) {
- it.setResult(context.config.global.betterLocation.coordinates.get().first) }
+ it.setResult(getLat()) }
hook("getLongitude", HookStage.BEFORE) {
- it.setResult(context.config.global.betterLocation.coordinates.get().second)
+ it.setResult(getLong())
}
}
}
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/ui/menu/impl/ChatActionMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/ui/menu/impl/ChatActionMenu.kt
@@ -172,4 +172,4 @@ class ChatActionMenu : AbstractMenu() {
viewGroup.addView(buttonContainer)
}
-}
+}+
\ No newline at end of file