+12
-1
README.md
+12
-1
README.md
···
25
- renaming `steam_id_remote` dictionnary key to `remote_steam_id` to fix network spam detection that resulted in timeouts
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
28
## How to make a mod?
29
30
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
32
{
33
"name": "Ship Mod", // Mod name
34
"author": "Estym", // Author
···
25
- renaming `steam_id_remote` dictionnary key to `remote_steam_id` to fix network spam detection that resulted in timeouts
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
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
+
39
## How to make a mod?
40
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:
42
+
```jsonc
43
{
44
"name": "Ship Mod", // Mod name
45
"author": "Estym", // Author
+20
-29
example_mods/ship/patch/player.patch
+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"
6
export var NPC_name = "NPC Test"
7
export var NPC_title = "npc title here"
8
9
-
+var ship_mod_instance = preload("res://Mods/Ship/ship.gd").new()
10
var camera_zoom = 5.0
11
12
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():
31
32
mouse_look = false
33
34
+ if ship_mod_instance.is_sitting_on_ship(self):
35
+ ship_mod_instance.process_ship(self, get_world())
36
+ return
37
if sitting: return
38
-
-
39
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):
43
return false
44
45
-
46
- if $detection_zones / prop_detect.get_overlapping_bodies().size() > 0 or not is_on_floor() or not $detection_zones / prop_ray.is_colliding():
47
- PlayerData._send_notification("invalid prop placement", 1)
48
- return false
···
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"
14
export var NPC_name = "NPC Test"
15
export var NPC_title = "npc title here"
16
17
+
+
18
var camera_zoom = 5.0
19
20
var direction = Vector3()
21
+
@@ -531,6 +533,10 @@ func _get_input():
22
23
mouse_look = false
24
25
+ if ship_mod_instance.is_sitting_on_ship(self):
26
+ ship_mod_instance.process_ship(self, get_world())
27
+ return
28
+
+
29
if sitting: return
30
+
31
if Input.is_action_pressed("move_forward"): direction -= cam_base.transform.basis.z
32
+
@@ -1389,17 +1395,6 @@ func _create_prop(ref, offset = Vector3(0, 1, 0), restrict_to_one = false):
33
+
PlayerData.emit_signal("_prop_update")
34
return false
35
36
+
-
37
- if $detection_zones / prop_detect.get_overlapping_bodies().size() > 0 or not is_on_floor() or not $detection_zones / prop_ray.is_colliding():
38
- PlayerData._send_notification("invalid prop placement", 1)
39
- return false
+7
modlist.md
+7
modlist.md
+140
-66
src/main.rs
+140
-66
src/main.rs
···
1
-
mod utils;
2
-
mod patches;
3
mod mods;
4
5
use std::env::{current_exe, set_current_dir};
6
use std::fs::File;
7
use std::io::{Read, Write};
8
use std::path::Path;
9
-
use std::process::Command;
10
use std::time::Duration;
11
-
use asky::Text;
12
-
use async_std::fs::create_dir;
13
use steamlocate::SteamDir;
14
use sudo::RunningAs;
15
use sysinfo::ProcessesToUpdate;
16
-
use godot_pck::structs::PCK;
17
18
static WEBFISHING_APPID: u32 = 3146520;
19
20
async fn install_webfishing(location: &SteamDir) {
21
let steam_location = location.path();
22
-
let acf_path = steam_location.join("steamapps").join(format!("appmanifest_{}.acf", WEBFISHING_APPID));
23
24
println!("Creating Webfishing ACF");
25
-
File::create(acf_path).unwrap().write(include_str!("../res/webfishing.acf").as_bytes()).expect("could not write acf");
26
27
println!("Waiting for steam to close");
28
let mut system = sysinfo::System::new_all();
···
40
}
41
42
println!("Steam launched, downloading webfishing");
43
-
let download_path = steam_location.join("steamapps").join("downloading").join(format!("{}", WEBFISHING_APPID));
44
45
while Path::exists(download_path.as_path()) {
46
println!("Downloading webfishing...");
···
50
51
async fn download_godot_steam_template() {
52
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");
54
let body = res.bytes().await.expect("Could not read body");
55
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");
58
}
59
60
async fn download_gd_decomp() {
···
69
Command::new("unzip")
70
.arg("decompiler.zip")
71
.current_dir("build")
72
-
.output().expect("Could not unzip godotsteam template");
73
}
74
75
fn build_webfishing_macos(webfishing_path: &Path) {
76
let template_path = Path::new("build/osx_template.app");
77
Command::new("rm")
78
.current_dir(template_path)
79
-
.arg("Contents/MacOS/godot_osx_debug.64")
80
-
.output().expect("Could not remove delete godot_osx_debug.64");
81
82
Command::new("mv")
83
.current_dir(template_path)
84
-
.arg("Contents/MacOS/godot_osx_release.64")
85
.arg("Contents/MacOS/webfishing")
86
-
.output().expect("Could not rename godot_osc_release.64");
87
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");
90
91
Command::new("cp")
92
.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");
95
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");
98
99
Command::new("mv")
100
.arg(template_path)
101
.arg(Path::new("build/webfishing.app"))
102
-
.output().expect("Could not copy webfishing.app");
103
}
104
105
#[tokio::main]
106
async fn main() {
107
-
set_current_dir(current_exe().unwrap().parent().expect("Could not get current dir")).expect("Could not set current dir");
108
if !Path::exists("build".as_ref()) {
109
println!("Creating build folder");
110
-
create_dir("build").await.expect("could not create build folder");
111
}
112
113
let location = SteamDir::locate().expect("could not locate steam directory");
···
118
install_webfishing(&location).await;
119
}
120
121
-
let (app, library) = location.find_app(WEBFISHING_APPID).unwrap().unwrap();
122
123
if !Path::exists("build/decompiler.zip".as_ref()) {
124
download_gd_decomp().await;
125
}
126
127
-
if !Path::exists("build/godot_steam_template.zip".as_ref()) {
128
download_godot_steam_template().await;
129
}
130
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
-
}
139
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
-
}
149
150
151
let binding = library.resolve_app_dir(&app);
152
let webfishing_path = binding.as_path();
···
154
build_webfishing_macos(webfishing_path);
155
}
156
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();
162
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");
167
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
-
}
171
172
-
sudo::escalate_if_needed().expect("Could not escalate to sign the app");
173
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 !");
181
182
-
Text::new("Press Enter to quit").prompt().expect("Could not confirm to quit");
183
}
···
1
mod mods;
2
+
mod patches;
3
+
mod utils;
4
5
+
use asky::Text;
6
+
use async_std::fs::create_dir;
7
+
use godot_pck::structs::PCK;
8
use std::env::{current_exe, set_current_dir};
9
use std::fs::File;
10
use std::io::{Read, Write};
11
use std::path::Path;
12
+
use std::process::{exit, Command};
13
use std::time::Duration;
14
use steamlocate::SteamDir;
15
use sudo::RunningAs;
16
use sysinfo::ProcessesToUpdate;
17
18
static WEBFISHING_APPID: u32 = 3146520;
19
20
async fn install_webfishing(location: &SteamDir) {
21
let steam_location = location.path();
22
+
let acf_path = steam_location
23
+
.join("steamapps")
24
+
.join(format!("appmanifest_{}.acf", WEBFISHING_APPID));
25
26
println!("Creating Webfishing ACF");
27
+
File::create(acf_path)
28
+
.unwrap()
29
+
.write(include_str!("../res/webfishing.acf").as_bytes())
30
+
.expect("could not write acf");
31
32
println!("Waiting for steam to close");
33
let mut system = sysinfo::System::new_all();
···
45
}
46
47
println!("Steam launched, downloading webfishing");
48
+
let download_path = steam_location
49
+
.join("steamapps")
50
+
.join("downloading")
51
+
.join(format!("{}", WEBFISHING_APPID));
52
53
while Path::exists(download_path.as_path()) {
54
println!("Downloading webfishing...");
···
58
59
async fn download_godot_steam_template() {
60
println!("Downloading 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");
66
let body = res.bytes().await.expect("Could not read body");
67
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");
72
}
73
74
async fn download_gd_decomp() {
···
83
Command::new("unzip")
84
.arg("decompiler.zip")
85
.current_dir("build")
86
+
.output()
87
+
.expect("Could not unzip godotsteam template");
88
}
89
90
fn build_webfishing_macos(webfishing_path: &Path) {
91
let template_path = Path::new("build/osx_template.app");
92
+
93
Command::new("rm")
94
.current_dir(template_path)
95
+
.arg("Contents/MacOS/godot_osx_debug.universal")
96
+
.output()
97
+
.expect("Could not remove delete godot_osx_debug.universal");
98
99
Command::new("mv")
100
.current_dir(template_path)
101
+
.arg("Contents/MacOS/godot_osx_release.universal")
102
.arg("Contents/MacOS/webfishing")
103
+
.output()
104
+
.expect("Could not rename godot_osc_release.universal");
105
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");
116
117
Command::new("cp")
118
.arg(webfishing_path.join("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");
127
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");
133
134
Command::new("mv")
135
.arg(template_path)
136
.arg(Path::new("build/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
+
160
}
161
162
#[tokio::main]
163
async fn main() {
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");
176
if !Path::exists("build".as_ref()) {
177
println!("Creating build folder");
178
+
create_dir("build")
179
+
.await
180
+
.expect("could not create build folder");
181
}
182
183
let location = SteamDir::locate().expect("could not locate steam directory");
···
188
install_webfishing(&location).await;
189
}
190
191
+
let (app, library) = location.find_app(WEBFISHING_APPID).unwrap().unwrap();
192
193
if !Path::exists("build/decompiler.zip".as_ref()) {
194
download_gd_decomp().await;
195
}
196
197
+
if !Path::exists("build/godot_steam_template_324.zip".as_ref()) {
198
download_godot_steam_template().await;
199
}
200
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");
208
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");
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");
223
224
let binding = library.resolve_app_dir(&app);
225
let webfishing_path = binding.as_path();
···
227
build_webfishing_macos(webfishing_path);
228
}
229
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();
237
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);
241
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");
247
248
+
File::create("build/signing-step").expect("Could not create signing step file");
249
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
+
}
255
256
+
sign_webfishing();
257
}
+1
-1
src/patches/steam_network_patch.rs
+1
-1
src/patches/steam_network_patch.rs
···
24
script.read_to_string(&mut script_txt).await.expect("Cannot read script");
25
drop(script);
26
27
-
let patched_script = script_txt.replace("steam_id_remote", "remote_steam_id");
28
let mut script = File::create(SCRIPT_PATH).await.expect("Cannot open script");
29
script.write_all(patched_script.as_bytes()).await.expect("Cannot write");
30
drop(script);
···
24
script.read_to_string(&mut script_txt).await.expect("Cannot read script");
25
drop(script);
26
27
+
let patched_script = script_txt.replace(".LOBBY_COMPARISON_EQUAL_TO_GREATER_THAN", ".OBBY_COMPARISON_EQUAL_TO_GREATER_THAN");
28
let mut script = File::create(SCRIPT_PATH).await.expect("Cannot open script");
29
script.write_all(patched_script.as_bytes()).await.expect("Cannot write");
30
drop(script);