+2
-2
.gitignore
+2
-2
.gitignore
+66
.tangled/workflows/build-publish.yaml
+66
.tangled/workflows/build-publish.yaml
···
1
+
when:
2
+
- event: ["push", "manual"]
3
+
branch: ["master"]
4
+
- event: ["push", "manual"]
5
+
branch: ["testing"]
6
+
7
+
engine: "nixery"
8
+
9
+
# using the default values
10
+
clone:
11
+
skip: false
12
+
depth: 1
13
+
submodules: false
14
+
15
+
dependencies:
16
+
# nixpkgs
17
+
nixpkgs:
18
+
- cargo
19
+
- dotnet-sdk
20
+
- mono
21
+
- wget
22
+
- unzip
23
+
- git
24
+
- gcc
25
+
26
+
steps:
27
+
# SETUP passwd
28
+
- name: "Create /etc/passwd"
29
+
command: "echo root:x:0:0::$PWD/home:/usr/bin/bash > /etc/passwd"
30
+
- name: "Create fake home"
31
+
command: "mkdir $PWD/home"
32
+
33
+
# GODOTSTEAM
34
+
- name: "Get GodotSteam"
35
+
command: "wget -nv https://codeberg.org/godotsteam/godotsteam/releases/download/v3.21/linux64-g352-s158-gs321.zip -O godotsteam.zip"
36
+
- name: "Unzip GodotSteam"
37
+
command: "unzip godotsteam.zip -d godotsteam"
38
+
- name: "Make GodotSteam executable"
39
+
command: "chmod +x godotsteam/linux-352-editor.64"
40
+
41
+
# GDWEAVE
42
+
- name: "Get GDWeave"
43
+
command: "wget -nv https://github.com/NotNite/GDWeave/releases/download/v2.0.14/GDWeave.zip -O gdweave.zip"
44
+
- name: "Unzip GDWeave"
45
+
command: "unzip gdweave.zip"
46
+
47
+
# Setup home dir
48
+
49
+
50
+
# MANIFESTATION
51
+
- name: "Install Manifestation"
52
+
command: "cargo install --git https://github.com/NotNite/manifestation.git"
53
+
54
+
- name: "Create Manifestation config dir"
55
+
command: "mkdir manifestation_config"
56
+
57
+
- name: "Add GodotSteam to manifestation config"
58
+
command: "echo godot_path = \\\"${PWD}/godotsteam/linux-352-editor.64\\\" > manifestation_config/config.toml"
59
+
- name: "Add GDWeave to manifestation config"
60
+
command: "echo gdweave_path = \\\"${PWD}/GDWeave\\\" >> manifestation_config/config.toml"
61
+
62
+
- name: "LOG"
63
+
command: "cat manifestation_config/config.toml"
64
+
65
+
- name: "Run manifestation"
66
+
command: "MANIFESTATION_CONFIG_DIR=$PWD/manifestation_config ~/.cargo/bin/manifestation ./manifestation.toml"
+37
Atproto/AtProtoSaveFactory.cs
+37
Atproto/AtProtoSaveFactory.cs
···
1
+
using GDWeave;
2
+
using GDWeave.Modding;
3
+
using Teemaw.Calico.LexicalTransformer;
4
+
5
+
namespace Atproto;
6
+
7
+
public class AtProtoSaveFactory
8
+
{
9
+
public static IScriptMod Create(IModInterface mod)
10
+
{
11
+
return new TransformationRuleScriptModBuilder()
12
+
.ForMod(mod)
13
+
.Named("AtProtoSave")
14
+
.Patching("res://Scenes/Singletons/UserSave/usersave.gdc")
15
+
.AddRule(new TransformationRuleBuilder()
16
+
.Named("ready_slot_condition")
17
+
.Matching(TransformationPatternFactory.CreateGdSnippetPattern("_load_save(last_loaded_slot)", 2))
18
+
.Do(Operation.ReplaceAll)
19
+
.With("""
20
+
var Atproto = $"/root/Atproto"
21
+
if last_loaded_slot != Atproto.ATPROTO_SLOT or Atproto.config.Autoload:
22
+
_load_save(last_loaded_slot)
23
+
else:
24
+
last_loaded_slot = -1
25
+
""", 2)
26
+
)
27
+
.AddRule(new TransformationRuleBuilder()
28
+
.Named("save_file")
29
+
.Matching(TransformationPatternFactory.CreateGdSnippetPattern(
30
+
"\"locked_refs\": PlayerData.locked_refs, \n\t}\n", 2))
31
+
.Do(Operation.Append)
32
+
.With("var atproto = $\"/root/Atproto\"\nif atproto.can_save_to_atproto():\n\tatproto.AtProtoClient.save_file()\n", 1)
33
+
)
34
+
35
+
.Build();
36
+
}
37
+
}
+1
-5
Atproto/Atproto.csproj
+1
-5
Atproto/Atproto.csproj
···
4
4
<ImplicitUsings>enable</ImplicitUsings>
5
5
<Nullable>enable</Nullable>
6
6
<AssemblySearchPaths>$(AssemblySearchPaths);$(GDWeavePath)/core</AssemblySearchPaths>
7
-
<Version>1.0.0.0</Version>
7
+
<Version>1.0.2.0</Version>
8
8
<RootNamespace>Atproto</RootNamespace>
9
9
</PropertyGroup>
10
10
11
11
<ItemGroup>
12
12
<Reference Include="GDWeave" Private="false"/>
13
13
<Reference Include="Serilog" Private="false"/>
14
-
</ItemGroup>
15
-
16
-
<ItemGroup>
17
-
<None Include="manifest.json" CopyToOutputDirectory="PreserveNewest"/>
18
14
</ItemGroup>
19
15
20
16
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(GDWeavePath)' != ''">
+3
Atproto/Config.cs
+3
Atproto/Config.cs
+1
Atproto/Mod.cs
+1
Atproto/Mod.cs
···
9
9
Config = modInterface.ReadConfig<Config>();
10
10
modInterface.RegisterScriptMod(CatchFishFactory.Create(modInterface));
11
11
modInterface.RegisterScriptMod(CatptureFishFactory.Create(modInterface));
12
+
modInterface.RegisterScriptMod(AtProtoSaveFactory.Create(modInterface));
12
13
}
13
14
14
15
public void Dispose() {
-14
Atproto/manifest.json
-14
Atproto/manifest.json
···
1
-
{
2
-
"Id": "Atproto",
3
-
"AssemblyPath": "Atproto.dll",
4
-
"Metadata": {
5
-
"Name": "Atproto Webfishing",
6
-
"Author": "Estym",
7
-
"Version": "1.0.0",
8
-
"Description": "A mod that sends data to your AtProto PDS."
9
-
},
10
-
"PackPath": "atproto.pck",
11
-
"Dependencies": [
12
-
"TackleBox"
13
-
]
14
-
}
+17
-5
README.md
+17
-5
README.md
···
1
-
# ATProto Webfishing
1
+
# AtProto Webfishing
2
2
3
3
A Webfishing mod that send data to your PDS
4
4
5
-
[Download Link](https://fb.dreamteam.boo/api/public/dl/oMcpvwyP/Webfishing-Atproto.zip)
5
+
[ThunderStore Link](https://thunderstore.io/c/webfishing/p/EstymMods/Webfishing_Atproto/)
6
+
7
+
# Features
8
+
- Remote save files
9
+
- Caught fish are saved to your PDS
6
10
7
11
## Requirements
8
12
- [GDWeave](https://thunderstore.io/c/webfishing/p/NotNet/GDWeave/)
9
13
- [TackleBox](https://thunderstore.io/c/webfishing/p/PuppyGirl/TackleBox/)
10
14
11
15
## Configuration
12
-
In game using the TackleBox mod menu, you can input your AtProto Handle as well as an App Password to login
13
-
After it's done, simply save and quit the game, you'll be connected on the next startup
16
+
In game using the AtProto menu, you can input your AtProto Handle as well as an App Password to login.
17
+
18
+
After it's done, you can select a save file or create one, then load it !
19
+
20
+
When a file is loaded, the savedata will be synchronized to your PDS each time the game saves.
21
+
22
+
## How to send your save online
23
+
24
+
To send your save on your PDS, simply create a new save and click on "Duplicate Save", it will create a new save on the PDS with the local data you have.
25
+
14
26
15
27
## Credits
16
28
17
-
- Thanks to [Tiany Ma](https://github.com/tma02) for his utils
29
+
- Thanks to [Tianyi Ma](https://github.com/tma02) for his utils
-135
gdscript/mods/Atproto/atproto_client.gd
-135
gdscript/mods/Atproto/atproto_client.gd
···
1
-
extends Node
2
-
3
-
# AtProto
4
-
var did
5
-
var pds
6
-
var accessJwt
7
-
var refreshJwt
8
-
9
-
10
-
# HTTP
11
-
var requester: HTTPRequest
12
-
13
-
func _enter_tree():
14
-
self.requester = HTTPRequest.new()
15
-
self.add_child(self.requester, true)
16
-
17
-
func is_token_expired() -> bool:
18
-
var json = Marshalls.base64_to_utf8(self.accessJwt.split(".")[1])
19
-
var data = parse_json(json)
20
-
var expires = data.exp
21
-
var unix = floor(Time.get_unix_time_from_system())
22
-
return expires < unix
23
-
24
-
func create_record(record):
25
-
26
-
if is_token_expired():
27
-
refresh_token("create_record", record)
28
-
return
29
-
30
-
var payload = {
31
-
repo = did,
32
-
collection = record.at_type,
33
-
record = record
34
-
}
35
-
36
-
var json_payload = JSON.print(payload)
37
-
json_payload = json_payload.replace("at_type", "$type")
38
-
39
-
var req = self.requester
40
-
41
-
var header = [
42
-
"Authorization: Bearer " + self.accessJwt,
43
-
"Content-Type: application/json"
44
-
]
45
-
46
-
req.request(pds + "/xrpc/com.atproto.repo.createRecord", header, true, HTTPClient.METHOD_POST, json_payload)
47
-
48
-
49
-
################
50
-
# LOGIN #
51
-
################
52
-
53
-
func login(handle, password):
54
-
var req = self.requester
55
-
56
-
req.connect("request_completed", self, "after_handle_resolver", [password])
57
-
req.request("https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=" + handle)
58
-
59
-
60
-
func after_handle_resolver(_result, _response_code, _headers, body: PoolByteArray, password):
61
-
var req = self.requester
62
-
req.disconnect("request_completed", self, "after_handle_resolver")
63
-
var res = parse_json(body.get_string_from_utf8())
64
-
self.did = res.did
65
-
66
-
req.connect("request_completed", self, "after_get_pds", [password])
67
-
req.request("https://plc.directory/" + self.did)
68
-
69
-
70
-
func after_get_pds(_result, _response_code, _headers, body: PoolByteArray, password):
71
-
var req = self.requester
72
-
req.disconnect("request_completed", self, "after_get_pds")
73
-
74
-
var res = parse_json(body.get_string_from_utf8())
75
-
for x in res.service:
76
-
if x.id == "#atproto_pds":
77
-
self.pds = x.serviceEndpoint
78
-
79
-
var payload = {
80
-
identifier = self.did,
81
-
password = password
82
-
}
83
-
84
-
req.connect("request_completed", self, "after_create_session")
85
-
req.request(pds + "/xrpc/com.atproto.server.createSession", ["Content-Type: application/json"], true, HTTPClient.METHOD_POST, JSON.print(payload))
86
-
87
-
88
-
89
-
func after_create_session(_result, _response_code, _headers, body: PoolByteArray):
90
-
var req = self.requester
91
-
req.disconnect("request_completed", self, "after_create_session")
92
-
93
-
var res = parse_json(body.get_string_from_utf8())
94
-
self.accessJwt = res.accessJwt
95
-
self.refreshJwt = res.refreshJwt
96
-
97
-
#######################
98
-
# REFRESH TOKEN #
99
-
#######################
100
-
func refresh_token(method = "", payload = ""):
101
-
var req = self.requester
102
-
103
-
var headers = [
104
-
"Authorization: Bearer " + self.refreshJwt,
105
-
"Content-Type: application/json"
106
-
]
107
-
req.connect("request_completed", self, "after_refresh_token", [method, payload])
108
-
req.request(pds + "/xrpc/com.atproto.server.refreshSession", headers, true, HTTPClient.METHOD_POST)
109
-
110
-
func after_refresh_token(_result, _response_code, _headers, body: PoolByteArray, method, payload):
111
-
var req = self.requester
112
-
req.disconnect("request_completed", self, "after_refresh_token")
113
-
114
-
var res = parse_json(body.get_string_from_utf8())
115
-
self.accessJwt = res.accessJwt
116
-
self.refreshJwt = res.refreshJwt
117
-
118
-
if method != "":
119
-
self.call(method, payload)
120
-
121
-
122
-
######################
123
-
# Method Calls #
124
-
######################
125
-
126
-
func catch_fish(fish, size, quality):
127
-
var fish_data = Globals.item_data[fish]["file"]
128
-
var record = {
129
-
at_type = "dev.regnault.webfishing.fish",
130
-
id = fish,
131
-
name = fish_data.item_name,
132
-
size = str(size),
133
-
quality = quality
134
-
}
135
-
create_record(record)
-36
gdscript/mods/Atproto/main.gd
-36
gdscript/mods/Atproto/main.gd
···
1
-
extends Node
2
-
3
-
var config: Dictionary
4
-
var default_config: Dictionary = {}
5
-
6
-
onready var TackleBox := $"/root/TackleBox"
7
-
const AtProtoClient_t := preload("res://mods/Atproto/atproto_client.gd")
8
-
var AtProtoClient: AtProtoClient_t
9
-
10
-
func _enter_tree():
11
-
AtProtoClient = AtProtoClient_t.new()
12
-
add_child(AtProtoClient)
13
-
14
-
func _ready() -> void:
15
-
TackleBox.connect("mod_config_updated", self, "_on_config_update")
16
-
_init_config()
17
-
18
-
func _init_config():
19
-
var saved_config = TackleBox.get_mod_config(name)
20
-
for key in default_config.keys():
21
-
if not saved_config[key]:
22
-
saved_config[key] = default_config[key]
23
-
config = saved_config
24
-
TackleBox.set_mod_config(name, config)
25
-
if config.Handle != "" and config.Password != "":
26
-
AtProtoClient.login(config.Handle, config.Password)
27
-
28
-
29
-
func _on_config_update(mod_id: String, new_config: Dictionary) -> void:
30
-
if mod_id != name:
31
-
return
32
-
if config.hash() == new_config.hash():
33
-
return
34
-
config = new_config
35
-
if config.Handle != "" and config.Password != "":
36
-
AtProtoClient.login(config.Handle, config.Password)
-21
gdscript/project.godot
-21
gdscript/project.godot
···
1
-
; Engine configuration file.
2
-
; It's best edited using the editor UI and not directly,
3
-
; since the parameters that go here are not all obvious.
4
-
;
5
-
; Format:
6
-
; [section] ; section goes between []
7
-
; param=value ; assign values to parameters
8
-
9
-
config_version=4
10
-
11
-
[application]
12
-
13
-
config/name="AtProto Webfishing"
14
-
15
-
[gui]
16
-
17
-
common/drop_mouse_on_gui_input_disabled=true
18
-
19
-
[physics]
20
-
21
-
common/enable_pause_aware_picking=true
icon.png
icon.png
This is a binary file and will not be displayed.
+36
lexicon/fish.json
+36
lexicon/fish.json
···
1
+
{
2
+
"id": "dev.regnault.webfishing.fish",
3
+
"defs": {
4
+
"main": {
5
+
"key": "tid",
6
+
"type": "record",
7
+
"output": {
8
+
"schema": {
9
+
"type": "object",
10
+
"required": [
11
+
"id",
12
+
"name",
13
+
"quality"
14
+
],
15
+
"properties": {
16
+
"id": {
17
+
"type": "string"
18
+
},
19
+
"name": {
20
+
"type": "string"
21
+
},
22
+
"size": {
23
+
"type": "string"
24
+
},
25
+
"quality": {
26
+
"type": "integer"
27
+
}
28
+
}
29
+
},
30
+
"encoding": "application/json"
31
+
}
32
+
}
33
+
},
34
+
"$type": "com.atproto.lexicon.schema",
35
+
"lexicon": 1
36
+
}
+448
lexicon/save.json
+448
lexicon/save.json
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "dev.regnault.webfishing.save",
4
+
"defs": {
5
+
"main": {
6
+
"type": "record",
7
+
"description": "Record declaring a save data of the game webfishing",
8
+
"key": "tid",
9
+
"record": {
10
+
"type": "object",
11
+
"required": [
12
+
"inventory"
13
+
],
14
+
"properties": {
15
+
"inventory": {
16
+
"type": "array",
17
+
"items": {
18
+
"type": "ref",
19
+
"ref": "#item"
20
+
}
21
+
},
22
+
"hotbar": {
23
+
"type": "ref",
24
+
"ref": "#hotbar"
25
+
},
26
+
"cosmetics_unlocked": {
27
+
"type": "array",
28
+
"items": {
29
+
"type": "string"
30
+
}
31
+
},
32
+
"cosmetics_equipped": {
33
+
"type": "ref",
34
+
"ref": "#cosmetics"
35
+
},
36
+
"new_cosmetics": {
37
+
"type": "array",
38
+
"items": {
39
+
"type": "string"
40
+
}
41
+
},
42
+
"version": {
43
+
"type": "string"
44
+
},
45
+
"money": {
46
+
"type": "integer"
47
+
},
48
+
"bait_inv": {
49
+
"type": "ref",
50
+
"ref": "#bait_inv"
51
+
},
52
+
"bait_selected": {
53
+
"type": "string"
54
+
},
55
+
"bait_unlocked": {
56
+
"type": "array",
57
+
"items": {
58
+
"type": "string"
59
+
}
60
+
},
61
+
"journal": {
62
+
"type": "array",
63
+
"items": {
64
+
"type": "ref",
65
+
"ref": "#journal_category"
66
+
}
67
+
},
68
+
"quests": {
69
+
"type": "array",
70
+
"items": {
71
+
"type": "ref",
72
+
"ref": "#quest_entry"
73
+
}
74
+
},
75
+
"completed_quests": {
76
+
"type": "array",
77
+
"items": {
78
+
"type": "string"
79
+
}
80
+
},
81
+
"level": {
82
+
"type": "integer"
83
+
},
84
+
"xp": {
85
+
"type": "integer"
86
+
},
87
+
"max_bait": {
88
+
"type": "integer"
89
+
},
90
+
"lure_unlocked": {
91
+
"type": "array",
92
+
"items": {
93
+
"type": "string"
94
+
}
95
+
},
96
+
"lure_selected": {
97
+
"type": "string"
98
+
},
99
+
"saved_aqua_fish": {
100
+
"type": "ref",
101
+
"ref": "#aqua_fish"
102
+
},
103
+
"inbound_mail": {
104
+
"type": "array",
105
+
"items": {
106
+
"type": "ref",
107
+
"ref": "#letter"
108
+
}
109
+
},
110
+
"rod_power": {
111
+
"type": "integer"
112
+
},
113
+
"rod_speed": {
114
+
"type": "integer"
115
+
},
116
+
"rod_chance": {
117
+
"type": "integer"
118
+
},
119
+
"rod_luck": {
120
+
"type": "integer"
121
+
},
122
+
"saved_tags": {
123
+
"type": "array",
124
+
"items": {
125
+
"type": "string"
126
+
}
127
+
},
128
+
"loan_level": {
129
+
"type": "integer"
130
+
},
131
+
"loan_left": {
132
+
"type": "integer"
133
+
},
134
+
"buddy_level": {
135
+
"type": "integer"
136
+
},
137
+
"buddy_speed": {
138
+
"type": "integer"
139
+
},
140
+
"guitar_shapes": {
141
+
"type": "array",
142
+
"items": {
143
+
"type": "ref",
144
+
"ref": "#guitar_shapes"
145
+
}
146
+
},
147
+
"fish_caught": {
148
+
"type": "integer"
149
+
},
150
+
"cash_total": {
151
+
"type": "integer"
152
+
},
153
+
"voice_pitch": {
154
+
"type": "string"
155
+
},
156
+
"voice_speed": {
157
+
"type": "integer"
158
+
},
159
+
160
+
"locked_refs": {
161
+
"type": "array",
162
+
"items": {
163
+
"type": "integer"
164
+
}
165
+
}
166
+
}
167
+
}
168
+
},
169
+
"item": {
170
+
"type": "object",
171
+
"properties": {
172
+
"id": {
173
+
"type": "string"
174
+
},
175
+
"ref": {
176
+
"type": "integer"
177
+
},
178
+
"size": {
179
+
"type": "string"
180
+
},
181
+
"quality": {
182
+
"type": "integer"
183
+
},
184
+
"tags": {
185
+
"type": "array",
186
+
"items": {
187
+
"type": "string"
188
+
}
189
+
},
190
+
"custom_name": {
191
+
"type": "string"
192
+
},
193
+
"count": {
194
+
"type": "integer"
195
+
}
196
+
}
197
+
},
198
+
"hotbar": {
199
+
"type": "object",
200
+
"properties": {
201
+
"0":{
202
+
"type": "integer"
203
+
},
204
+
"1":{
205
+
"type": "integer"
206
+
},
207
+
"2":{
208
+
"type": "integer"
209
+
},
210
+
"3":{
211
+
"type": "integer"
212
+
},
213
+
"4":{
214
+
"type": "integer"
215
+
}
216
+
}
217
+
},
218
+
"cosmetics": {
219
+
"type": "object",
220
+
"properties": {
221
+
"species": {
222
+
"type": "string"
223
+
},
224
+
"pattern": {
225
+
"type": "string"
226
+
},
227
+
"primary_color": {
228
+
"type": "string"
229
+
},
230
+
"secondary_color": {
231
+
"type": "string"
232
+
},
233
+
"hat": {
234
+
"type": "string"
235
+
},
236
+
"undershirt": {
237
+
"type": "string"
238
+
},
239
+
"overshirt":{
240
+
"type": "string"
241
+
},
242
+
"title": {
243
+
"type": "string"
244
+
},
245
+
"bobber": {
246
+
"type": "string"
247
+
},
248
+
"eye": {
249
+
"type": "string"
250
+
},
251
+
"nose": {
252
+
"type": "string"
253
+
},
254
+
"mouth": {
255
+
"type": "string"
256
+
},
257
+
"accessory": {
258
+
"type": "array",
259
+
"items": {
260
+
"type": "string"
261
+
}
262
+
},
263
+
"tail": {
264
+
"type": "string"
265
+
},
266
+
"legs": {
267
+
"type": "string"
268
+
}
269
+
}
270
+
},
271
+
"bait_inv": {
272
+
"type": "object",
273
+
"properties": {
274
+
"": {
275
+
"type": "integer"
276
+
},
277
+
"worms": {
278
+
"type": "integer"
279
+
},
280
+
"cricket": {
281
+
"type": "integer"
282
+
},
283
+
"leech": {
284
+
"type": "integer"
285
+
},
286
+
"minnow": {
287
+
"type": "integer"
288
+
},
289
+
"squid": {
290
+
"type": "integer"
291
+
},
292
+
"nautilus": {
293
+
"type": "integer"
294
+
}
295
+
}
296
+
},
297
+
"journal_category": {
298
+
"type": "object",
299
+
"properties": {
300
+
"name": {
301
+
"type": "string"
302
+
},
303
+
"entries": {
304
+
"type": "array",
305
+
"items": {
306
+
"type": "ref",
307
+
"ref": "#journal_entry"
308
+
}
309
+
}
310
+
}
311
+
},
312
+
"journal_entry": {
313
+
"type": "object",
314
+
"properties": {
315
+
"name": {
316
+
"type": "string"
317
+
},
318
+
"count": {
319
+
"type": "integer"
320
+
},
321
+
"record": {
322
+
"type": "string"
323
+
},
324
+
"quality": {
325
+
"type": "array",
326
+
"items": {
327
+
"type": "integer"
328
+
}
329
+
}
330
+
}
331
+
},
332
+
"quest_entry": {
333
+
"type": "object",
334
+
"properties": {
335
+
"id": {
336
+
"type": "integer"
337
+
},
338
+
"title": {
339
+
"type": "string"
340
+
},
341
+
"tier": {
342
+
"type": "integer"
343
+
},
344
+
"action": {
345
+
"type": "string"
346
+
},
347
+
"gold_reward": {
348
+
"type": "integer"
349
+
},
350
+
"xp_reward": {
351
+
"type": "integer"
352
+
},
353
+
"rewards": {
354
+
"type": "array",
355
+
"items": {
356
+
"type": "string"
357
+
}
358
+
},
359
+
"goal_id": {
360
+
"type": "string"
361
+
},
362
+
"icon": {
363
+
"type": "string"
364
+
},
365
+
"progress": {
366
+
"type": "integer"
367
+
},
368
+
"max_level": {
369
+
"type": "integer"
370
+
},
371
+
"hidden": {
372
+
"type": "boolean"
373
+
},
374
+
"goal_amt": {
375
+
"type": "integer"
376
+
},
377
+
"goal_array": {
378
+
"type": "array",
379
+
"items": {
380
+
"type": "integer"
381
+
}
382
+
}
383
+
}
384
+
},
385
+
"aqua_fish": {
386
+
"type": "object",
387
+
"properties": {
388
+
"id": {
389
+
"type": "string"
390
+
},
391
+
"size": {
392
+
"type": "string"
393
+
},
394
+
"ref": {
395
+
"type": "integer"
396
+
},
397
+
"quality": {
398
+
"type": "integer"
399
+
}
400
+
}
401
+
},
402
+
"guitar_shapes": {
403
+
"type": "array",
404
+
"items": {
405
+
"type": "ref",
406
+
"ref": "#guitar_shape"
407
+
}
408
+
},
409
+
"guitar_shape": {
410
+
"type": "array",
411
+
"maxLength": 6,
412
+
"minLength": 6,
413
+
"items": {
414
+
"type": "integer"
415
+
}
416
+
},
417
+
"letter": {
418
+
"type": "object",
419
+
"properties": {
420
+
"letter_id": {
421
+
"type": "integer"
422
+
},
423
+
"header": {
424
+
"type": "string"
425
+
},
426
+
"closing": {
427
+
"type": "string"
428
+
},
429
+
"body": {
430
+
"type": "string"
431
+
},
432
+
"items": {
433
+
"type": "array",
434
+
"items": {
435
+
"type": "ref",
436
+
"ref": "#item"
437
+
}
438
+
},
439
+
"to": {
440
+
"type": "string"
441
+
},
442
+
"from": {
443
+
"type": "string"
444
+
}
445
+
}
446
+
}
447
+
}
448
+
}
+27
lexicon/savefile.json
+27
lexicon/savefile.json
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "dev.regnault.webfishing.savefile",
4
+
"defs": {
5
+
"main": {
6
+
"type": "record",
7
+
"description": "Record declaring a savefile of Webfishing.",
8
+
"key": "tid",
9
+
"record": {
10
+
"type": "object",
11
+
"required": [
12
+
"name",
13
+
"uri"
14
+
],
15
+
"properties": {
16
+
"name": {
17
+
"type": "string"
18
+
},
19
+
"uri": {
20
+
"type": "string",
21
+
"format": "at-uri"
22
+
}
23
+
}
24
+
}
25
+
}
26
+
}
27
+
}
+20
manifestation.toml
+20
manifestation.toml
···
1
+
id = "Atproto"
2
+
name = "Atproto Webfishing"
3
+
description = "A mod that sends data to your AtProto PDS."
4
+
version = "1.0.3"
5
+
homepage = "https://tangled.org/@regnault.dev/webfishing-atproto"
6
+
author = "Estym"
7
+
8
+
icon = "icon.png"
9
+
readme = "README.md"
10
+
11
+
[project]
12
+
csharp = "./Atproto/Atproto.csproj"
13
+
godot = "./project/project.godot"
14
+
15
+
[[dependencies]]
16
+
thunderstore_version = "NotNet-GDWeave-2.0.12"
17
+
18
+
[[dependencies]]
19
+
id = "TackleBox"
20
+
thunderstore_version = "PuppyGirl-TackleBox-0.5.2"
+264
project/mods/Atproto/assets/at_proto.tres
+264
project/mods/Atproto/assets/at_proto.tres
···
1
+
[gd_resource type="Theme" load_steps=28 format=2]
2
+
3
+
[ext_resource path="res://Assets/Textures/UI/scrollbar.png" type="Texture" id=1]
4
+
[ext_resource path="res://Assets/Themes/main_font.tres" type="DynamicFont" id=2]
5
+
6
+
[sub_resource type="StyleBoxFlat" id=1]
7
+
content_margin_left = 12.0
8
+
content_margin_right = 12.0
9
+
bg_color = Color( 0.415686, 0.266667, 0.12549, 1 )
10
+
border_color = Color( 0.415686, 0.266667, 0.12549, 1 )
11
+
corner_radius_top_left = 12
12
+
corner_radius_top_right = 12
13
+
corner_radius_bottom_right = 12
14
+
corner_radius_bottom_left = 12
15
+
corner_detail = 5
16
+
17
+
[sub_resource type="StyleBoxEmpty" id=34]
18
+
19
+
[sub_resource type="StyleBoxFlat" id=30]
20
+
content_margin_left = 12.0
21
+
content_margin_right = 12.0
22
+
bg_color = Color( 0.611765, 0.568627, 0.290196, 1 )
23
+
border_color = Color( 0.835294, 0.666667, 0.45098, 1 )
24
+
corner_radius_top_left = 12
25
+
corner_radius_top_right = 12
26
+
corner_radius_bottom_right = 12
27
+
corner_radius_bottom_left = 12
28
+
corner_detail = 5
29
+
expand_margin_left = 1.0
30
+
expand_margin_right = 1.0
31
+
expand_margin_top = 1.0
32
+
expand_margin_bottom = 1.0
33
+
34
+
[sub_resource type="StyleBoxFlat" id=31]
35
+
content_margin_left = 12.0
36
+
content_margin_right = 12.0
37
+
bg_color = Color( 0.352941, 0.458824, 0.352941, 1 )
38
+
border_color = Color( 0.415686, 0.266667, 0.12549, 1 )
39
+
corner_radius_top_left = 12
40
+
corner_radius_top_right = 12
41
+
corner_radius_bottom_right = 12
42
+
corner_radius_bottom_left = 12
43
+
44
+
[sub_resource type="StyleBoxFlat" id=32]
45
+
content_margin_left = 12.0
46
+
content_margin_right = 12.0
47
+
bg_color = Color( 0.352941, 0.458824, 0.352941, 1 )
48
+
border_color = Color( 0.415686, 0.266667, 0.12549, 1 )
49
+
corner_radius_top_left = 12
50
+
corner_radius_top_right = 12
51
+
corner_radius_bottom_right = 12
52
+
corner_radius_bottom_left = 12
53
+
corner_detail = 5
54
+
55
+
[sub_resource type="StyleBoxFlat" id=18]
56
+
bg_color = Color( 0.352941, 0.458824, 0.352941, 1 )
57
+
border_width_left = 6
58
+
border_color = Color( 1, 1, 1, 0 )
59
+
corner_radius_top_left = 4
60
+
corner_radius_top_right = 4
61
+
corner_radius_bottom_right = 4
62
+
corner_radius_bottom_left = 4
63
+
64
+
[sub_resource type="StyleBoxFlat" id=15]
65
+
bg_color = Color( 0.611765, 0.568627, 0.290196, 1 )
66
+
border_color = Color( 0.835294, 0.666667, 0.45098, 1 )
67
+
corner_radius_top_left = 12
68
+
corner_radius_top_right = 12
69
+
corner_radius_bottom_right = 12
70
+
corner_radius_bottom_left = 12
71
+
corner_detail = 5
72
+
73
+
[sub_resource type="StyleBoxFlat" id=21]
74
+
bg_color = Color( 0.0627451, 0.109804, 0.192157, 1 )
75
+
draw_center = false
76
+
border_width_left = 16
77
+
border_color = Color( 1, 1, 1, 0 )
78
+
corner_radius_top_left = 4
79
+
corner_radius_top_right = 4
80
+
corner_radius_bottom_right = 4
81
+
corner_radius_bottom_left = 4
82
+
83
+
[sub_resource type="StyleBoxEmpty" id=20]
84
+
85
+
[sub_resource type="StyleBoxFlat" id=35]
86
+
87
+
[sub_resource type="StyleBoxFlat" id=36]
88
+
89
+
[sub_resource type="StyleBoxFlat" id=37]
90
+
bg_color = Color( 0.0627451, 0.109804, 0.192157, 1 )
91
+
corner_radius_top_left = 8
92
+
corner_radius_top_right = 8
93
+
corner_radius_bottom_right = 8
94
+
corner_radius_bottom_left = 8
95
+
expand_margin_top = 3.0
96
+
expand_margin_bottom = 3.0
97
+
98
+
[sub_resource type="StyleBoxFlat" id=33]
99
+
content_margin_left = 12.0
100
+
content_margin_right = 12.0
101
+
bg_color = Color( 0.572549, 0.45098, 0.290196, 1 )
102
+
border_color = Color( 0.415686, 0.266667, 0.12549, 1 )
103
+
corner_radius_top_left = 12
104
+
corner_radius_top_right = 12
105
+
corner_radius_bottom_right = 12
106
+
corner_radius_bottom_left = 12
107
+
108
+
[sub_resource type="StyleBoxFlat" id=9]
109
+
bg_color = Color( 1, 0.933333, 0.835294, 1 )
110
+
border_color = Color( 0.415686, 0.266667, 0.12549, 1 )
111
+
corner_radius_top_left = 32
112
+
corner_radius_top_right = 32
113
+
corner_radius_bottom_right = 32
114
+
corner_radius_bottom_left = 32
115
+
116
+
[sub_resource type="ImageTexture" id=24]
117
+
118
+
[sub_resource type="StyleBoxFlat" id=25]
119
+
content_margin_left = 4.0
120
+
content_margin_right = 4.0
121
+
bg_color = Color( 0.611765, 0.568627, 0.290196, 1 )
122
+
corner_radius_top_left = 8
123
+
corner_radius_top_right = 8
124
+
corner_radius_bottom_right = 8
125
+
corner_radius_bottom_left = 8
126
+
expand_margin_left = 2.0
127
+
expand_margin_right = 2.0
128
+
expand_margin_top = 2.0
129
+
130
+
[sub_resource type="StyleBoxEmpty" id=26]
131
+
132
+
[sub_resource type="StyleBoxEmpty" id=27]
133
+
134
+
[sub_resource type="StyleBoxFlat" id=28]
135
+
content_margin_left = 8.0
136
+
content_margin_right = 8.0
137
+
bg_color = Color( 0.352941, 0.458824, 0.352941, 1 )
138
+
corner_radius_bottom_right = 12
139
+
corner_radius_bottom_left = 12
140
+
expand_margin_top = 10.0
141
+
expand_margin_bottom = 8.0
142
+
143
+
[sub_resource type="StyleBoxEmpty" id=29]
144
+
145
+
[sub_resource type="StyleBoxFlat" id=4]
146
+
bg_color = Color( 0.352941, 0.458824, 0.352941, 1 )
147
+
border_color = Color( 0.415686, 0.266667, 0.12549, 1 )
148
+
corner_radius_top_left = 12
149
+
corner_radius_top_right = 12
150
+
corner_radius_bottom_right = 12
151
+
corner_radius_bottom_left = 12
152
+
expand_margin_left = 2.0
153
+
expand_margin_right = 2.0
154
+
155
+
[sub_resource type="StyleBoxFlat" id=8]
156
+
bg_color = Color( 0.835294, 0.666667, 0.45098, 1 )
157
+
border_width_left = 2
158
+
border_width_top = 2
159
+
border_width_right = 2
160
+
border_width_bottom = 2
161
+
border_color = Color( 0.415686, 0.266667, 0.12549, 1 )
162
+
corner_radius_top_left = 6
163
+
corner_radius_top_right = 6
164
+
corner_radius_bottom_right = 6
165
+
corner_radius_bottom_left = 6
166
+
corner_detail = 5
167
+
anti_aliasing = false
168
+
169
+
[sub_resource type="StyleBoxFlat" id=6]
170
+
bg_color = Color( 0.835294, 0.666667, 0.45098, 1 )
171
+
border_width_left = 2
172
+
border_width_top = 2
173
+
border_width_right = 2
174
+
border_color = Color( 0.415686, 0.266667, 0.12549, 1 )
175
+
corner_radius_top_left = 6
176
+
corner_radius_top_right = 6
177
+
corner_detail = 5
178
+
anti_aliasing = false
179
+
180
+
[sub_resource type="StyleBoxFlat" id=7]
181
+
bg_color = Color( 0.835294, 0.666667, 0.45098, 1 )
182
+
border_width_left = 2
183
+
border_width_top = 2
184
+
border_width_right = 2
185
+
border_color = Color( 0.415686, 0.266667, 0.12549, 1 )
186
+
corner_radius_top_left = 6
187
+
corner_radius_top_right = 6
188
+
corner_detail = 5
189
+
expand_margin_bottom = 2.0
190
+
anti_aliasing = false
191
+
192
+
[sub_resource type="StyleBoxEmpty" id=23]
193
+
194
+
[resource]
195
+
default_font = ExtResource( 2 )
196
+
Button/colors/font_color = Color( 1, 0.933333, 0.835294, 1 )
197
+
Button/colors/font_color_disabled = Color( 0.835294, 0.666667, 0.45098, 1 )
198
+
Button/colors/font_color_focus = Color( 1, 0.933333, 0.835294, 1 )
199
+
Button/colors/font_color_hover = Color( 1, 0.933333, 0.835294, 1 )
200
+
Button/colors/font_color_pressed = Color( 0.835294, 0.666667, 0.45098, 1 )
201
+
Button/styles/disabled = SubResource( 1 )
202
+
Button/styles/focus = SubResource( 34 )
203
+
Button/styles/hover = SubResource( 30 )
204
+
Button/styles/normal = SubResource( 31 )
205
+
Button/styles/pressed = SubResource( 32 )
206
+
HScrollBar/styles/grabber = SubResource( 18 )
207
+
HScrollBar/styles/grabber_highlight = SubResource( 15 )
208
+
HScrollBar/styles/grabber_pressed = SubResource( 15 )
209
+
HScrollBar/styles/scroll = SubResource( 21 )
210
+
HScrollBar/styles/scroll_focus = SubResource( 20 )
211
+
HSlider/icons/grabber = ExtResource( 1 )
212
+
HSlider/icons/grabber_disabled = ExtResource( 1 )
213
+
HSlider/icons/grabber_highlight = ExtResource( 1 )
214
+
HSlider/styles/grabber_area = SubResource( 35 )
215
+
HSlider/styles/grabber_area_highlight = SubResource( 36 )
216
+
HSlider/styles/slider = SubResource( 37 )
217
+
Label/colors/font_color = Color( 0.352941, 0.458824, 0.352941, 1 )
218
+
LineEdit/colors/cursor_color = Color( 1, 0.933333, 0.835294, 1 )
219
+
LineEdit/colors/font_color = Color( 1, 0.933333, 0.835294, 1 )
220
+
LineEdit/colors/font_color_selected = Color( 1, 0.933333, 0.835294, 1 )
221
+
LineEdit/colors/font_color_uneditable = Color( 0.835294, 0.666667, 0.45098, 1 )
222
+
LineEdit/styles/focus = SubResource( 33 )
223
+
LineEdit/styles/normal = SubResource( 33 )
224
+
LineEdit/styles/read_only = SubResource( 33 )
225
+
Panel/styles/panel = SubResource( 9 )
226
+
PopupMenu/colors/font_color = Color( 1, 0.933333, 0.835294, 1 )
227
+
PopupMenu/colors/font_color_hover = Color( 1, 0.933333, 0.835294, 1 )
228
+
PopupMenu/icons/checked = SubResource( 24 )
229
+
PopupMenu/icons/radio_checked = SubResource( 24 )
230
+
PopupMenu/icons/radio_unchecked = SubResource( 24 )
231
+
PopupMenu/icons/submenu = SubResource( 24 )
232
+
PopupMenu/icons/unchecked = SubResource( 24 )
233
+
PopupMenu/styles/hover = SubResource( 25 )
234
+
PopupMenu/styles/labeled_separator_left = SubResource( 26 )
235
+
PopupMenu/styles/labeled_separator_right = SubResource( 27 )
236
+
PopupMenu/styles/panel = SubResource( 28 )
237
+
PopupMenu/styles/panel_disabled = SubResource( 28 )
238
+
PopupMenu/styles/separator = SubResource( 29 )
239
+
ProgressBar/colors/font_color = Color( 1, 0.933333, 0.835294, 1 )
240
+
ProgressBar/styles/bg = SubResource( 1 )
241
+
ProgressBar/styles/fg = SubResource( 4 )
242
+
RichTextLabel/colors/default_color = Color( 0.835294, 0.666667, 0.45098, 1 )
243
+
TabContainer/colors/font_color_bg = Color( 0.415686, 0.266667, 0.12549, 1 )
244
+
TabContainer/colors/font_color_disabled = Color( 0.611765, 0.0784314, 0.0784314, 1 )
245
+
TabContainer/colors/font_color_fg = Color( 1, 0.933333, 0.835294, 1 )
246
+
TabContainer/styles/panel = SubResource( 8 )
247
+
TabContainer/styles/tab_bg = SubResource( 6 )
248
+
TabContainer/styles/tab_disabled = SubResource( 1 )
249
+
TabContainer/styles/tab_fg = SubResource( 7 )
250
+
Tabs/colors/font_color_bg = Color( 0.835294, 0.666667, 0.45098, 1 )
251
+
Tabs/colors/font_color_fg = Color( 0.835294, 0.666667, 0.45098, 1 )
252
+
TextEdit/colors/caret_color = Color( 1, 0.933333, 0.835294, 1 )
253
+
TextEdit/colors/font_color = Color( 0.415686, 0.266667, 0.12549, 1 )
254
+
TextEdit/colors/font_color_readonly = Color( 0.415686, 0.266667, 0.12549, 1 )
255
+
TextEdit/colors/selection_color = Color( 0.352941, 0.458824, 0.352941, 1 )
256
+
TextEdit/styles/completion = SubResource( 23 )
257
+
TextEdit/styles/focus = SubResource( 23 )
258
+
TextEdit/styles/normal = SubResource( 23 )
259
+
TextEdit/styles/read_only = SubResource( 23 )
260
+
VScrollBar/styles/grabber = SubResource( 18 )
261
+
VScrollBar/styles/grabber_highlight = SubResource( 15 )
262
+
VScrollBar/styles/grabber_pressed = SubResource( 15 )
263
+
VScrollBar/styles/scroll = SubResource( 21 )
264
+
VScrollBar/styles/scroll_focus = SubResource( 20 )
+462
project/mods/Atproto/atproto_client.gd
+462
project/mods/Atproto/atproto_client.gd
···
1
+
extends Node
2
+
3
+
# Signals
4
+
signal connection(suceeded)
5
+
signal savefile_loaded(uri)
6
+
7
+
# State
8
+
var can_save = true
9
+
10
+
# AtProto
11
+
var did
12
+
var pds
13
+
var accessJwt
14
+
var refreshJwt
15
+
var Atproto
16
+
17
+
func _enter_tree():
18
+
Atproto = self.get_parent()
19
+
20
+
func connected() -> bool:
21
+
return accessJwt != null
22
+
23
+
func is_token_expired() -> bool:
24
+
var token_data = self.accessJwt.split(".")[1]
25
+
var data = null
26
+
for x in ["", "=", "=="] :
27
+
var json = Marshalls.base64_to_utf8(token_data + x)
28
+
data = parse_json(json)
29
+
if data != null: break
30
+
var expires = data.exp
31
+
var unix = floor(Time.get_unix_time_from_system())
32
+
return expires < unix
33
+
34
+
func get_header():
35
+
return [
36
+
"Authorization: Bearer " + self.accessJwt,
37
+
"Content-Type: application/json"
38
+
]
39
+
40
+
func create_record(record, callback : FuncRef = null):
41
+
42
+
if is_token_expired():
43
+
refresh_token("create_record", [record])
44
+
return
45
+
46
+
var payload = {
47
+
repo = did,
48
+
collection = record.at_type,
49
+
record = record
50
+
}
51
+
52
+
var json_payload = JSON.print(payload)
53
+
json_payload = json_payload.replace("at_type", "$type")
54
+
55
+
var req = HTTPRequest.new()
56
+
self.add_child(req)
57
+
req.connect("request_completed", self, "_create_record_handler", [req, callback])
58
+
req.request(pds + "/xrpc/com.atproto.repo.createRecord", get_header(), true, HTTPClient.METHOD_POST, json_payload)
59
+
60
+
61
+
func _create_record_handler(_result, code, _headers, body: PoolByteArray, req: HTTPRequest, callback: FuncRef):
62
+
req.queue_free()
63
+
if callback == null:
64
+
return
65
+
66
+
var res = parse_json(body.get_string_from_utf8())
67
+
callback.call_func(res)
68
+
69
+
## LIST RECORDS
70
+
71
+
func list_records(callback: FuncRef, collection: String, limit: int = 50, cursor = ""):
72
+
var query_string = "repo=" + did
73
+
query_string += "&collection=" + collection.http_escape()
74
+
query_string += "&limit=" + str(limit).http_escape()
75
+
query_string += "&cursor=" + str(limit).http_escape()
76
+
var req_str = pds + "/xrpc/com.atproto.repo.listRecords?" + query_string
77
+
78
+
var req = HTTPRequest.new()
79
+
self.add_child(req)
80
+
req.connect("request_completed", self, "_list_record_handler", [req, callback])
81
+
req.request(req_str, get_header(), true, HTTPClient.METHOD_GET)
82
+
83
+
func _list_record_handler(_result, code, _headers, body: PoolByteArray, req: HTTPRequest, callback: FuncRef):
84
+
req.queue_free()
85
+
var b = body.get_string_from_utf8()
86
+
var res = parse_json(b)
87
+
callback.call_func(res.records)
88
+
89
+
90
+
## GET RECORD
91
+
92
+
func get_record(callback: FuncRef, did: String, collection: String, rkey: String):
93
+
var query_string = "repo=" + did.http_escape()
94
+
query_string += "&collection=" + collection.http_escape()
95
+
query_string += "&rkey=" + rkey.http_escape()
96
+
97
+
var req = HTTPRequest.new()
98
+
self.add_child(req)
99
+
req.connect("request_completed", self, "_get_record_handler", [req, callback])
100
+
req.request(pds + "/xrpc/com.atproto.repo.getRecord?" + query_string, get_header(), true, HTTPClient.METHOD_GET)
101
+
102
+
func _get_record_handler(_result, code, _headers, body: PoolByteArray, req: HTTPRequest, callback: FuncRef):
103
+
req.queue_free()
104
+
var res = parse_json(body.get_string_from_utf8())
105
+
callback.call_func(res)
106
+
107
+
108
+
## PUT RECORD
109
+
110
+
func put_record(uri, record, callback: FuncRef = null):
111
+
if is_token_expired():
112
+
refresh_token("put_record", [uri, record])
113
+
return
114
+
115
+
var splitted_uri = uri.split("/")
116
+
117
+
118
+
var payload = {
119
+
repo = splitted_uri[2],
120
+
collection = splitted_uri[3],
121
+
rkey = splitted_uri[4],
122
+
record = record
123
+
}
124
+
125
+
var json_payload = JSON.print(payload)
126
+
json_payload = json_payload.replace("at_type", "$type")
127
+
128
+
var req = HTTPRequest.new()
129
+
self.add_child(req)
130
+
req.connect("request_completed", self, "_put_record_handler", [req, callback])
131
+
req.request(pds + "/xrpc/com.atproto.repo.putRecord", get_header(), true, HTTPClient.METHOD_POST, json_payload)
132
+
133
+
func _put_record_handler(_result, code, _headers, body: PoolByteArray, req: HTTPRequest, callback: FuncRef):
134
+
req.queue_free()
135
+
if callback == null:
136
+
return
137
+
var res = parse_json(body.get_string_from_utf8())
138
+
callback.call_func(res)
139
+
140
+
################
141
+
# LOGIN #
142
+
################
143
+
144
+
func login(handle, password):
145
+
var req = HTTPRequest.new()
146
+
self.add_child(req)
147
+
148
+
req.connect("request_completed", self, "after_handle_resolver", [req, password])
149
+
req.request("https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=" + handle)
150
+
151
+
152
+
func after_handle_resolver(_result, code, _headers, body: PoolByteArray, req: HTTPRequest, password):
153
+
req.disconnect("request_completed", self, "after_handle_resolver")
154
+
155
+
if code != 200:
156
+
emit_signal("connection", false)
157
+
return
158
+
159
+
var res = parse_json(body.get_string_from_utf8())
160
+
self.did = res.did
161
+
162
+
req.connect("request_completed", self, "after_get_pds", [req, password])
163
+
req.request("https://plc.directory/" + self.did)
164
+
165
+
166
+
func after_get_pds(_result, code, _headers, body: PoolByteArray,req: HTTPRequest, password):
167
+
req.disconnect("request_completed", self, "after_get_pds")
168
+
169
+
if code != 200:
170
+
emit_signal("connection", false)
171
+
return
172
+
173
+
var res = parse_json(body.get_string_from_utf8())
174
+
for x in res.service:
175
+
if x.id == "#atproto_pds":
176
+
self.pds = x.serviceEndpoint
177
+
178
+
var payload = {
179
+
identifier = self.did,
180
+
password = password
181
+
}
182
+
183
+
req.connect("request_completed", self, "after_create_session", [req])
184
+
req.request(pds + "/xrpc/com.atproto.server.createSession", ["Content-Type: application/json"], true, HTTPClient.METHOD_POST, JSON.print(payload))
185
+
186
+
187
+
func after_create_session(_result, code, _headers, body: PoolByteArray, req: HTTPRequest):
188
+
req.queue_free()
189
+
190
+
if code != 200:
191
+
emit_signal("connection", false)
192
+
return
193
+
194
+
var res = parse_json(body.get_string_from_utf8())
195
+
self.accessJwt = res.accessJwt
196
+
self.refreshJwt = res.refreshJwt
197
+
emit_signal("connection", true)
198
+
199
+
#######################
200
+
# REFRESH TOKEN #
201
+
#######################
202
+
func refresh_token(method = "", payload = []):
203
+
var req = self.requester
204
+
205
+
var headers = [
206
+
"Authorization: Bearer " + self.refreshJwt,
207
+
"Content-Type: application/json"
208
+
]
209
+
req.connect("request_completed", self, "after_refresh_token", [method, payload])
210
+
req.request(pds + "/xrpc/com.atproto.server.refreshSession", headers, true, HTTPClient.METHOD_POST)
211
+
212
+
func after_refresh_token(_result, _response_code, _headers, body: PoolByteArray, method, payload):
213
+
var req = self.requester
214
+
req.disconnect("request_completed", self, "after_refresh_token")
215
+
216
+
var res = parse_json(body.get_string_from_utf8())
217
+
self.accessJwt = res.accessJwt
218
+
self.refreshJwt = res.refreshJwt
219
+
220
+
if method != "":
221
+
self.callv(method, payload)
222
+
223
+
224
+
######################
225
+
# Method Calls #
226
+
######################
227
+
228
+
func catch_fish(fish, size, quality):
229
+
var fish_data = Globals.item_data[fish]["file"]
230
+
var record = {
231
+
at_type = "dev.regnault.webfishing.fish",
232
+
id = fish,
233
+
name = fish_data.item_name,
234
+
size = str(size),
235
+
quality = quality
236
+
}
237
+
create_record(record)
238
+
239
+
240
+
# SAVES
241
+
242
+
func create_save_file(uri: String, filename: String, callback: FuncRef = null):
243
+
var record = {
244
+
at_type = "dev.regnault.webfishing.savefile",
245
+
uri = uri,
246
+
name = filename,
247
+
}
248
+
create_record(record, callback)
249
+
pass
250
+
251
+
func get_saves(callback: FuncRef):
252
+
list_records(callback, "dev.regnault.webfishing.savefile")
253
+
254
+
255
+
func get_save_data():
256
+
var save_data = {
257
+
"inventory": PlayerData.inventory,
258
+
"hotbar": PlayerData.hotbar,
259
+
"cosmetics_unlocked": PlayerData.cosmetics_unlocked,
260
+
"cosmetics_equipped": PlayerData.cosmetics_equipped,
261
+
"new_cosmetics": [],
262
+
"version": Globals.GAME_VERSION,
263
+
"muted_players": PlayerData.players_muted,
264
+
"hidden_players": PlayerData.players_hidden,
265
+
"recorded_time": PlayerData.last_recorded_time,
266
+
"money": PlayerData.money,
267
+
"bait_inv": PlayerData.bait_inv,
268
+
"bait_selected": PlayerData.bait_selected,
269
+
"bait_unlocked": PlayerData.bait_unlocked,
270
+
"shop": PlayerData.current_shop,
271
+
"journal": PlayerData.journal_logs,
272
+
"quests": PlayerData.current_quests,
273
+
"completed_quests": PlayerData.completed_quests,
274
+
"level": PlayerData.badge_level,
275
+
"xp": PlayerData.badge_xp,
276
+
"max_bait": PlayerData.max_bait,
277
+
"lure_unlocked": PlayerData.lure_unlocked,
278
+
"lure_selected": PlayerData.lure_selected,
279
+
"saved_aqua_fish": PlayerData.saved_aqua_fish,
280
+
"inbound_mail": PlayerData.inbound_mail,
281
+
"rod_power": PlayerData.rod_power_level,
282
+
"rod_speed": PlayerData.rod_speed_level,
283
+
"rod_chance": PlayerData.rod_chance_level,
284
+
"rod_luck": PlayerData.rod_luck_level,
285
+
"saved_tags": PlayerData.saved_tags,
286
+
"loan_level": PlayerData.loan_level,
287
+
"loan_left": PlayerData.loan_left,
288
+
"buddy_level": PlayerData.buddy_level,
289
+
"buddy_speed": PlayerData.buddy_speed,
290
+
"guitar_shapes": PlayerData.guitar_shapes,
291
+
"fish_caught": PlayerData.fish_caught,
292
+
"cash_total": PlayerData.cash_total,
293
+
"voice_pitch": PlayerData.voice_pitch,
294
+
"voice_speed": PlayerData.voice_speed,
295
+
"locked_refs": PlayerData.locked_refs,
296
+
}
297
+
save_data = save_data.duplicate(true)
298
+
299
+
# JOURNAL
300
+
var modified_journal = []
301
+
for area in save_data.journal:
302
+
var area_entry = {
303
+
name = area,
304
+
entries = []
305
+
}
306
+
for entry_name in save_data.journal[area]:
307
+
var entry = save_data.journal[area][entry_name]
308
+
area_entry.entries.append({
309
+
name = entry_name,
310
+
count = entry.count,
311
+
record = str(entry.record),
312
+
quality = entry.quality
313
+
})
314
+
modified_journal.append(area_entry)
315
+
save_data.journal = modified_journal
316
+
317
+
# Quests
318
+
var modified_quests = []
319
+
for quest_id in save_data.quests:
320
+
var entry = save_data.quests[quest_id].duplicate(true)
321
+
entry.id = quest_id
322
+
modified_quests.append(entry)
323
+
save_data.quests = modified_quests
324
+
325
+
# Inventory
326
+
for item in save_data.inventory:
327
+
item.size = str(item.size)
328
+
329
+
330
+
# Version
331
+
save_data.version = str(save_data.version)
332
+
333
+
# Voice Pitch
334
+
save_data.voice_pitch = str(save_data.voice_pitch)
335
+
336
+
# Aqua Fish
337
+
save_data.saved_aqua_fish.size = str(save_data.saved_aqua_fish.size)
338
+
339
+
# Letters
340
+
for letter in save_data.inbound_mail:
341
+
for item in letter.items:
342
+
item.size = str(item.size)
343
+
344
+
return save_data
345
+
346
+
func save_file(callback: FuncRef = null, creation = false):
347
+
if UserSave.current_loaded_slot != Atproto.ATPROTO_SLOT:
348
+
return
349
+
if !connected(): return
350
+
351
+
var save_data = get_save_data()
352
+
save_data.at_type = "dev.regnault.webfishing.save"
353
+
354
+
if Atproto.save_loaded != "":
355
+
put_record(Atproto.save_loaded, save_data)
356
+
357
+
func create_save(callback: FuncRef = null):
358
+
if !connected(): return
359
+
var save_data = get_save_data()
360
+
save_data.at_type = "dev.regnault.webfishing.save"
361
+
create_record(save_data, callback)
362
+
363
+
func load_save(uri: String):
364
+
var splitted_uri = uri.split("/")
365
+
var did = splitted_uri[2]
366
+
var collection = splitted_uri[3]
367
+
var rkey = splitted_uri[4]
368
+
get_record(funcref(self, "_after_get_save"), did, collection, rkey)
369
+
pass
370
+
371
+
func _after_get_save(save_record):
372
+
var save = save_record.value
373
+
374
+
UserSave._load_save(Atproto.ATPROTO_SLOT)
375
+
376
+
var modified_journal: Dictionary = {}
377
+
for area in save.journal:
378
+
var area_entries = {}
379
+
for entry in area.entries:
380
+
area_entries[entry.name] = {
381
+
count = entry.count,
382
+
record = float(entry.record),
383
+
quality = entry.quality
384
+
}
385
+
386
+
modified_journal[area.name] = area_entries
387
+
save.journal = modified_journal
388
+
389
+
var modified_quests = {}
390
+
for quest in save.quests:
391
+
var id = quest.id
392
+
modified_quests[quest.id] = quest
393
+
modified_quests[quest.id].erase("id")
394
+
save.quests = modified_quests
395
+
396
+
# Inventory
397
+
for item in save.inventory:
398
+
item.size = float(item.size)
399
+
item.quality = int(item.quality)
400
+
var x = PlayerData.QUALITY_DATA[item.quality]
401
+
402
+
save.version = float(save.version)
403
+
save.saved_aqua_fish.size = float(save.saved_aqua_fish.size)
404
+
for letter in save.inbound_mail:
405
+
for item in letter.items:
406
+
item.size = float(item.size)
407
+
item.quality = int(item.quality)
408
+
var x = PlayerData.QUALITY_DATA[item.quality]
409
+
410
+
var modified_hotbar = {}
411
+
for item in save.hotbar:
412
+
modified_hotbar[int(item)] = save.hotbar[item]
413
+
save.hotbar = modified_hotbar
414
+
415
+
PlayerData.inventory = save.inventory
416
+
PlayerData.hotbar = save.hotbar
417
+
PlayerData.cosmetics_unlocked = save.cosmetics_unlocked
418
+
PlayerData.cosmetics_equipped = save.cosmetics_equipped
419
+
PlayerData.money = save.money
420
+
PlayerData.players_muted = save.muted_players
421
+
PlayerData.players_hidden = save.hidden_players
422
+
PlayerData.bait_inv = save.bait_inv
423
+
PlayerData.bait_selected = save.bait_selected
424
+
PlayerData.bait_unlocked = save.bait_unlocked
425
+
PlayerData.max_bait = save.max_bait
426
+
PlayerData.lure_unlocked = save.lure_unlocked
427
+
PlayerData.lure_selected = save.lure_selected
428
+
PlayerData.journal_logs = save.journal
429
+
PlayerData.current_quests = save.quests
430
+
PlayerData.completed_quests = save.completed_quests
431
+
PlayerData.badge_level = int(save.level)
432
+
PlayerData.badge_xp = int(save.xp)
433
+
PlayerData.saved_aqua_fish = save.saved_aqua_fish
434
+
PlayerData.inbound_mail = save.inbound_mail
435
+
PlayerData.saved_tags = save.saved_tags
436
+
PlayerData.loan_level = int(save.loan_level)
437
+
PlayerData.loan_left = save.loan_left
438
+
PlayerData.rod_power_level = save.rod_power
439
+
PlayerData.rod_speed_level = save.rod_speed
440
+
PlayerData.rod_chance_level = save.rod_chance
441
+
PlayerData.rod_luck_level = save.rod_luck
442
+
PlayerData.buddy_level = save.buddy_level
443
+
PlayerData.buddy_speed = save.buddy_speed
444
+
PlayerData.guitar_shapes = save.guitar_shapes
445
+
PlayerData.fish_caught = save.fish_caught
446
+
PlayerData.cash_total = save.cash_total
447
+
PlayerData.voice_pitch = float(save.voice_pitch)
448
+
PlayerData.voice_speed = save.voice_speed
449
+
PlayerData.locked_refs = save.locked_refs
450
+
PlayerData._validate_guitar_shapes()
451
+
PlayerData._validate_inventory()
452
+
PlayerData._journal_check()
453
+
PlayerData._missing_quest_check()
454
+
PlayerData._unlock_defaults()
455
+
456
+
can_save = false
457
+
UserSave._save_slot(Atproto.ATPROTO_SLOT)
458
+
can_save = true
459
+
460
+
emit_signal("savefile_loaded", save_record.uri)
461
+
462
+
+68
project/mods/Atproto/main.gd
+68
project/mods/Atproto/main.gd
···
1
+
extends Node
2
+
3
+
var config: Dictionary
4
+
5
+
const ATPROTO_SLOT = 99
6
+
var save_loaded: String
7
+
var default_config: Dictionary = {}
8
+
9
+
onready var TackleBox := $"/root/TackleBox"
10
+
const AtProtoClient_t := preload("res://mods/Atproto/atproto_client.gd")
11
+
var AtProtoClient: AtProtoClient_t
12
+
13
+
# UI
14
+
const AtProtoMenu := preload("res://mods/Atproto/ui/menus/atproto_config.tscn")
15
+
const AtProtoButton := preload("res://mods/Atproto/ui/buttons/atproto.tscn")
16
+
17
+
var setuped = false
18
+
19
+
func _enter_tree():
20
+
AtProtoClient = AtProtoClient_t.new()
21
+
add_child(AtProtoClient)
22
+
AtProtoClient.connect("savefile_loaded", self, "set_save_file")
23
+
get_tree().connect("node_added", self, "_add_atproto_menu")
24
+
25
+
26
+
func _ready() -> void:
27
+
_init_config()
28
+
29
+
func _init_config():
30
+
var saved_config = TackleBox.get_mod_config(name)
31
+
for key in default_config.keys():
32
+
if not saved_config[key]:
33
+
saved_config[key] = default_config[key]
34
+
config = saved_config
35
+
TackleBox.set_mod_config(name, config)
36
+
if config.Autoconnect == true:
37
+
AtProtoClient.login(config.Handle, config.Password)
38
+
39
+
func _save_config():
40
+
TackleBox.set_mod_config(name, config)
41
+
42
+
func _add_atproto_menu(node: Node):
43
+
if node.name == "main_menu":
44
+
var atproto_menu: Node = AtProtoMenu.instance()
45
+
atproto_menu.visible = false
46
+
node.add_child(atproto_menu)
47
+
48
+
var button = AtProtoButton.instance()
49
+
var menu_list: Node = node.get_node("VBoxContainer")
50
+
var settings_button: Node = menu_list.get_node("settings")
51
+
menu_list.add_child(button)
52
+
menu_list.move_child(button, settings_button.get_index() + 1)
53
+
atproto_menu.connect("setup_done", self, "_after_setup")
54
+
pass
55
+
56
+
func _after_setup():
57
+
if setuped:
58
+
return
59
+
setuped = true
60
+
61
+
if config.Save != "" and config.Autoload and AtProtoClient.connected():
62
+
AtProtoClient.load_save(config.Save)
63
+
64
+
func can_save_to_atproto():
65
+
return AtProtoClient.can_save && UserSave.current_loaded_slot == ATPROTO_SLOT && AtProtoClient.connected()
66
+
67
+
func set_save_file(save_uri):
68
+
save_loaded = save_uri
+21
project/project.godot
+21
project/project.godot
···
1
+
; Engine configuration file.
2
+
; It's best edited using the editor UI and not directly,
3
+
; since the parameters that go here are not all obvious.
4
+
;
5
+
; Format:
6
+
; [section] ; section goes between []
7
+
; param=value ; assign values to parameters
8
+
9
+
config_version=4
10
+
11
+
[application]
12
+
13
+
config/name="AtProto Webfishing"
14
+
15
+
[gui]
16
+
17
+
common/drop_mouse_on_gui_input_disabled=true
18
+
19
+
[physics]
20
+
21
+
common/enable_pause_aware_picking=true