A native webfishing installer for macos

Compare changes

Choose any two refs to compare.

Changed files
+180 -97
example_mods
ship
patch
src
+12 -1
README.md
··· 25 25 - renaming `steam_id_remote` dictionnary key to `remote_steam_id` to fix network spam detection that resulted in timeouts 26 26 - prevent the game from crashing when saving the options by not setting any values to `OS.windowed_borderless` because setting a value to it crashes the game somehow 27 27 28 + 29 + ## How to install a mod? 30 + 31 + When running the software for the first time and building webfishing, you'll notice that a `mods` folder has appeared in the folder where the installer is. 32 + 33 + In order to install a mod, just copy a mod folder in it, a mod folder has a `manifest.json` file in it. 34 + 35 + After that, run the installer again ! It will tell you in the terminal if the mod is installed or if something went wrong. 36 + 37 + Here's a small mod list : [link to the mod list](modlist.md) 38 + 28 39 ## How to make a mod? 29 40 30 41 As you can see in the `example_mods` folder, a mod has typically two folders and a single `manifest.json` file having the following structure: 31 - ```json 42 + ```jsonc 32 43 { 33 44 "name": "Ship Mod", // Mod name 34 45 "author": "Estym", // Author
+20 -29
example_mods/ship/patch/player.patch
··· 1 - diff --git a/webfishing/Scenes/Entities/Player/player.gd b/Users/evann/Godot/Webfishing/Scenes/Entities/Player/player.gd 2 - index 3879ff8..cc20048 100644 3 - --- a/webfishing/Scenes/Entities/Player/player.gd 4 - +++ b/Users/evann/Godot/Webfishing/Scenes/Entities/Player/player.gd 5 - @@ -30,6 +30,7 @@ export var NPC_cosmetics = {"species": "species_cat", "pattern": "pattern_none" 1 + diff --git a/player.gd b/player copie.gd 2 + index c84af29..4cb41f9 100644 3 + --- a/player.gd 4 + +++ b/player copie.gd 5 + @@ -23,6 +23,7 @@ const PARTICLE_DATA = { 6 + "music": preload("res://Scenes/Particles/music_particle.tscn"), 7 + "kiss": preload("res://Scenes/Particles/kiss.tscn"), 8 + } 9 + +var ship_mod_instance = preload("res://Mods/Ship/ship.gd").new() 10 + 11 + export (NodePath) var hand_sprite_node 12 + export (NodePath) var hand_bone_node 13 + @@ -31,6 +32,7 @@ export var NPC_cosmetics = {"species": "species_cat", "pattern": "pattern_none" 6 14 export var NPC_name = "NPC Test" 7 15 export var NPC_title = "npc title here" 8 16 9 - +var ship_mod_instance = preload("res://Mods/Ship/ship.gd").new() 17 + + 10 18 var camera_zoom = 5.0 11 19 12 20 var direction = Vector3() 13 - @@ -341,6 +342,7 @@ func _process(delta): 14 - 15 - func _controlled_process(delta): 16 - _get_input() 17 - + 18 - _process_movement(delta) 19 - _process_timers() 20 - _interact_check() 21 - @@ -480,7 +482,7 @@ func _process_timers(): 22 - 23 - func _get_input(): 24 - direction = Vector3.ZERO 25 - - 26 - + 27 - if Input.is_action_just_released("primary_action"): _primary_action_release() 28 - if Input.is_action_pressed("primary_action"): _primary_action_hold() 29 - else: primary_hold_timer = 0 30 - @@ -538,8 +540,10 @@ func _get_input(): 21 + @@ -531,6 +533,10 @@ func _get_input(): 31 22 32 23 mouse_look = false 33 24 34 25 + if ship_mod_instance.is_sitting_on_ship(self): 35 26 + ship_mod_instance.process_ship(self, get_world()) 36 27 + return 28 + + 37 29 if sitting: return 38 - - 30 + 39 31 if Input.is_action_pressed("move_forward"): direction -= cam_base.transform.basis.z 40 - if Input.is_action_pressed("move_back"): direction += cam_base.transform.basis.z 41 - if Input.is_action_pressed("move_right"): direction += cam_base.transform.basis.x 42 - @@ -1405,16 +1409,6 @@ func _create_prop(ref, offset = Vector3(0, 1, 0), restrict_to_one = false): 32 + @@ -1389,17 +1395,6 @@ func _create_prop(ref, offset = Vector3(0, 1, 0), restrict_to_one = false): 33 + PlayerData.emit_signal("_prop_update") 43 34 return false 44 35 45 - 36 + - 46 37 - if $detection_zones / prop_detect.get_overlapping_bodies().size() > 0 or not is_on_floor() or not $detection_zones / prop_ray.is_colliding(): 47 38 - PlayerData._send_notification("invalid prop placement", 1) 48 39 - return false
+7
modlist.md
··· 1 + # List of currently existing mods compatible with the webfishing-macos-installer 2 + 3 + ## AtProto Webfishing 4 + [Repository](https://forgejo.regnault.dev/estym/webfishing-macos-atproto) 5 + 6 + A mod that adds remote saving using a Bluesky account (or a self-hosted PDS) 7 +
+140 -66
src/main.rs
··· 1 - mod utils; 2 - mod patches; 3 1 mod mods; 2 + mod patches; 3 + mod utils; 4 4 5 + use asky::Text; 6 + use async_std::fs::create_dir; 7 + use godot_pck::structs::PCK; 5 8 use std::env::{current_exe, set_current_dir}; 6 9 use std::fs::File; 7 10 use std::io::{Read, Write}; 8 11 use std::path::Path; 9 - use std::process::Command; 12 + use std::process::{exit, Command}; 10 13 use std::time::Duration; 11 - use asky::Text; 12 - use async_std::fs::create_dir; 13 14 use steamlocate::SteamDir; 14 15 use sudo::RunningAs; 15 16 use sysinfo::ProcessesToUpdate; 16 - use godot_pck::structs::PCK; 17 17 18 18 static WEBFISHING_APPID: u32 = 3146520; 19 19 20 20 async fn install_webfishing(location: &SteamDir) { 21 21 let steam_location = location.path(); 22 - let acf_path = steam_location.join("steamapps").join(format!("appmanifest_{}.acf", WEBFISHING_APPID)); 22 + let acf_path = steam_location 23 + .join("steamapps") 24 + .join(format!("appmanifest_{}.acf", WEBFISHING_APPID)); 23 25 24 26 println!("Creating Webfishing ACF"); 25 - File::create(acf_path).unwrap().write(include_str!("../res/webfishing.acf").as_bytes()).expect("could not write acf"); 27 + File::create(acf_path) 28 + .unwrap() 29 + .write(include_str!("../res/webfishing.acf").as_bytes()) 30 + .expect("could not write acf"); 26 31 27 32 println!("Waiting for steam to close"); 28 33 let mut system = sysinfo::System::new_all(); ··· 40 45 } 41 46 42 47 println!("Steam launched, downloading webfishing"); 43 - let download_path = steam_location.join("steamapps").join("downloading").join(format!("{}", WEBFISHING_APPID)); 48 + let download_path = steam_location 49 + .join("steamapps") 50 + .join("downloading") 51 + .join(format!("{}", WEBFISHING_APPID)); 44 52 45 53 while Path::exists(download_path.as_path()) { 46 54 println!("Downloading webfishing..."); ··· 50 58 51 59 async fn download_godot_steam_template() { 52 60 println!("Downloading GodotSteam template..."); 53 - let res = reqwest::get("https://github.com/GodotSteam/GodotSteam/releases/download/v3.27/macos-g36-s160-gs327.zip").await.expect("Could not download godotsteam template"); 61 + let res = reqwest::get( 62 + "https://codeberg.org/godotsteam/godotsteam/releases/download/v3.24/macos-g353-s159-gs324.zip", 63 + ) 64 + .await 65 + .expect("Could not download godotsteam template"); 54 66 let body = res.bytes().await.expect("Could not read body"); 55 67 56 - let mut file = File::create("build/godot_steam_template.zip").expect("Could not create godotsteam template"); 57 - file.write_all(&body).expect("Could not write godotsteam template"); 68 + let mut file = File::create("build/godot_steam_template_324.zip") 69 + .expect("Could not create godotsteam template"); 70 + file.write_all(&body) 71 + .expect("Could not write godotsteam template"); 58 72 } 59 73 60 74 async fn download_gd_decomp() { ··· 69 83 Command::new("unzip") 70 84 .arg("decompiler.zip") 71 85 .current_dir("build") 72 - .output().expect("Could not unzip godotsteam template"); 86 + .output() 87 + .expect("Could not unzip godotsteam template"); 73 88 } 74 89 75 90 fn build_webfishing_macos(webfishing_path: &Path) { 76 91 let template_path = Path::new("build/osx_template.app"); 92 + 77 93 Command::new("rm") 78 94 .current_dir(template_path) 79 - .arg("Contents/MacOS/godot_osx_debug.64") 80 - .output().expect("Could not remove delete godot_osx_debug.64"); 95 + .arg("Contents/MacOS/godot_osx_debug.universal") 96 + .output() 97 + .expect("Could not remove delete godot_osx_debug.universal"); 81 98 82 99 Command::new("mv") 83 100 .current_dir(template_path) 84 - .arg("Contents/MacOS/godot_osx_release.64") 101 + .arg("Contents/MacOS/godot_osx_release.universal") 85 102 .arg("Contents/MacOS/webfishing") 86 - .output().expect("Could not rename godot_osc_release.64"); 103 + .output() 104 + .expect("Could not rename godot_osc_release.universal"); 87 105 88 - let mut steamappid = File::create(template_path.join("Contents").join("MacOS").join("steam_appid.txt")).expect("could not create steam_appid.txt file"); 89 - steamappid.write(include_str!("../res/steam_appid.txt").as_bytes()).expect("could not write steam_appid.txt"); 106 + let mut steamappid = File::create( 107 + template_path 108 + .join("Contents") 109 + .join("MacOS") 110 + .join("steam_appid.txt"), 111 + ) 112 + .expect("could not create steam_appid.txt file"); 113 + steamappid 114 + .write(include_str!("../res/steam_appid.txt").as_bytes()) 115 + .expect("could not write steam_appid.txt"); 90 116 91 117 Command::new("cp") 92 118 .arg(webfishing_path.join("webfishing.exe")) 93 - .arg(template_path.join("Contents").join("Resources").join("webfishing.pck")) 94 - .output().expect("Could not copy webfishing.exe"); 119 + .arg( 120 + template_path 121 + .join("Contents") 122 + .join("Resources") 123 + .join("webfishing.pck"), 124 + ) 125 + .output() 126 + .expect("Could not copy webfishing.exe"); 95 127 96 - let mut info_plist = File::create(template_path.join("Contents").join("Info.plist")).expect("Could not open Info.plist"); 97 - info_plist.write_all(include_str!("../res/Info.plist").as_bytes()).expect("could not write Info.plist"); 128 + let mut info_plist = File::create(template_path.join("Contents").join("Info.plist")) 129 + .expect("Could not open Info.plist"); 130 + info_plist 131 + .write_all(include_str!("../res/Info.plist").as_bytes()) 132 + .expect("could not write Info.plist"); 98 133 99 134 Command::new("mv") 100 135 .arg(template_path) 101 136 .arg(Path::new("build/webfishing.app")) 102 - .output().expect("Could not copy webfishing.app"); 137 + .output() 138 + .expect("Could not copy webfishing.app"); 139 + } 140 + 141 + fn sign_webfishing() { 142 + Command::new("xattr") 143 + .arg("-cr") 144 + .arg("build/webfishing.app") 145 + .output() 146 + .expect("Could not execute xattr"); 147 + 148 + Command::new("rm") 149 + .arg("build/signing-step") 150 + .output() 151 + .expect("Could not remove signing-step file"); 152 + 153 + println!("Webfishing is in the build folder !"); 154 + 155 + Text::new("Press Enter to quit") 156 + .prompt() 157 + .expect("Could not confirm to quit"); 158 + 159 + 103 160 } 104 161 105 162 #[tokio::main] 106 163 async fn main() { 107 - set_current_dir(current_exe().unwrap().parent().expect("Could not get current dir")).expect("Could not set current dir"); 164 + if sudo::check() == RunningAs::Root && Path::new("build/signing-step").exists() { 165 + sign_webfishing(); 166 + exit(0); 167 + } 168 + 169 + set_current_dir( 170 + current_exe() 171 + .unwrap() 172 + .parent() 173 + .expect("Could not get current dir"), 174 + ) 175 + .expect("Could not set current dir"); 108 176 if !Path::exists("build".as_ref()) { 109 177 println!("Creating build folder"); 110 - create_dir("build").await.expect("could not create build folder"); 178 + create_dir("build") 179 + .await 180 + .expect("could not create build folder"); 111 181 } 112 182 113 183 let location = SteamDir::locate().expect("could not locate steam directory"); ··· 118 188 install_webfishing(&location).await; 119 189 } 120 190 121 - let (app, library) = location.find_app(WEBFISHING_APPID).unwrap().unwrap(); 191 + let (app, library) = location.find_app(WEBFISHING_APPID).unwrap().unwrap(); 122 192 123 193 if !Path::exists("build/decompiler.zip".as_ref()) { 124 194 download_gd_decomp().await; 125 195 } 126 196 127 - if !Path::exists("build/godot_steam_template.zip".as_ref()) { 197 + if !Path::exists("build/godot_steam_template_324.zip".as_ref()) { 128 198 download_godot_steam_template().await; 129 199 } 130 200 131 - if !Path::exists("build/macos.zip".as_ref()) { 132 - println!("Unzipping template"); 133 - Command::new("unzip") 134 - .arg("-o") 135 - .arg("godot_steam_template.zip") 136 - .current_dir("./build") 137 - .output().expect("Could not unzip godot_steam_template.zip"); 138 - } 201 + println!("Unzipping template 1/2"); 202 + Command::new("unzip") 203 + .arg("-o") 204 + .arg("godot_steam_template_324.zip") 205 + .current_dir("./build") 206 + .output() 207 + .expect("Could not unzip godot_steam_template_324.zip"); 139 208 140 - if !Path::exists("build/osx_template.app".as_ref()) && !Path::exists("build/webfishing.app".as_ref()) { 141 - println!("Unzipping template"); 142 - Command::new("unzip") 143 - .arg("-o") 144 - .arg("macos.zip") 145 - .current_dir("./build") 146 - .output() 147 - .expect("Could not unzip macos.zip"); 148 - } 209 + Command::new("mv") 210 + .arg("build/godot_steam_template_324/macos.zip") 211 + .arg("build/macos.zip") 212 + .current_dir("./build") 213 + .output() 214 + .expect("Could not copy godot_steam_template_324/macos.zip"); 149 215 216 + println!("Unzipping template 2/2"); 217 + Command::new("unzip") 218 + .arg("-o") 219 + .arg("macos.zip") 220 + .current_dir("./build") 221 + .output() 222 + .expect("Could not unzip macos.zip"); 150 223 151 224 let binding = library.resolve_app_dir(&app); 152 225 let webfishing_path = binding.as_path(); ··· 154 227 build_webfishing_macos(webfishing_path); 155 228 } 156 229 157 - if sudo::check()!= RunningAs::Root { 158 - let _ = create_dir("build/webfishing-export").await; 159 - let mut bytes = vec![]; 160 - File::open(webfishing_path.join("webfishing.exe")).unwrap().read_to_end(&mut bytes).unwrap(); 161 - let mut pck = PCK::from_bytes(&*bytes).unwrap(); 230 + let _ = create_dir("build/webfishing-export").await; 231 + let mut bytes = vec![]; 232 + File::open(webfishing_path.join("webfishing.exe")) 233 + .unwrap() 234 + .read_to_end(&mut bytes) 235 + .unwrap(); 236 + let mut pck = PCK::from_bytes(&*bytes).unwrap(); 162 237 163 - patches::steam_network_patch::patch(&mut pck).await; 164 - patches::options_menu_patch::patch(&mut pck).await; 165 - mods::mods::process_mods(&mut pck); 166 - println!("Root permissions needed to sign webfishing"); 238 + patches::steam_network_patch::patch(&mut pck).await; 239 + patches::options_menu_patch::patch(&mut pck).await; 240 + mods::mods::process_mods(&mut pck); 167 241 168 - let bytes = &pck.to_bytes(); 169 - File::create("build/webfishing.app/Contents/Resources/webfishing.pck").unwrap().write(bytes).expect("Could not write to webfishing.pck"); 170 - } 242 + let bytes = &pck.to_bytes(); 243 + File::create("build/webfishing.app/Contents/Resources/webfishing.pck") 244 + .unwrap() 245 + .write(bytes) 246 + .expect("Could not write to webfishing.pck"); 171 247 172 - sudo::escalate_if_needed().expect("Could not escalate to sign the app"); 248 + File::create("build/signing-step").expect("Could not create signing step file"); 173 249 174 - Command::new("xattr") 175 - .arg("-cr") 176 - .arg("build/webfishing.app") 177 - .output() 178 - .expect("Could not execute xattr"); 179 - 180 - println!("Webfishing is in the build folder !"); 250 + if sudo::check() != RunningAs::Root { 251 + println!("In order to sign the app, you need to be root"); 252 + sudo::escalate_if_needed().expect("Could not escalate"); 253 + exit(1); 254 + } 181 255 182 - Text::new("Press Enter to quit").prompt().expect("Could not confirm to quit"); 256 + sign_webfishing(); 183 257 }
+1 -1
src/patches/steam_network_patch.rs
··· 24 24 script.read_to_string(&mut script_txt).await.expect("Cannot read script"); 25 25 drop(script); 26 26 27 - let patched_script = script_txt.replace("steam_id_remote", "remote_steam_id"); 27 + let patched_script = script_txt.replace(".LOBBY_COMPARISON_EQUAL_TO_GREATER_THAN", ".OBBY_COMPARISON_EQUAL_TO_GREATER_THAN"); 28 28 let mut script = File::create(SCRIPT_PATH).await.expect("Cannot open script"); 29 29 script.write_all(patched_script.as_bytes()).await.expect("Cannot write"); 30 30 drop(script);