Skip to content

Commit 5f1e79c

Browse files
authored
Merge pull request #134 from TaloDev/develop
Release 0.33.0
2 parents e8ad075 + 65dd450 commit 5f1e79c

File tree

14 files changed

+202
-31
lines changed

14 files changed

+202
-31
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
with:
1616
lfs: true
1717

18-
- uses: chickensoft-games/setup-godot@v1
18+
- uses: chickensoft-games/setup-godot@v2
1919
name: Set up Godot
2020
with:
2121
version: 4.4.1
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: Claude Code Review
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize]
6+
# Optional: Only run on specific file changes
7+
# paths:
8+
# - "src/**/*.ts"
9+
# - "src/**/*.tsx"
10+
# - "src/**/*.js"
11+
# - "src/**/*.jsx"
12+
13+
jobs:
14+
claude-review:
15+
# Optional: Filter by PR author
16+
# if: |
17+
# github.event.pull_request.user.login == 'external-contributor' ||
18+
# github.event.pull_request.user.login == 'new-developer' ||
19+
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
20+
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read
24+
pull-requests: read
25+
issues: read
26+
id-token: write
27+
28+
steps:
29+
- name: Checkout repository
30+
uses: actions/checkout@v4
31+
with:
32+
fetch-depth: 1
33+
34+
- name: Run Claude Code Review
35+
id: claude-review
36+
uses: anthropics/claude-code-action@beta
37+
with:
38+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39+
40+
# Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4.1)
41+
# model: "claude-opus-4-1-20250805"
42+
43+
# Direct prompt for automated review (no @claude mention needed)
44+
direct_prompt: |
45+
Please review this pull request and provide feedback on:
46+
- Code quality and best practices
47+
- Potential bugs or issues
48+
- Performance considerations
49+
- Security concerns
50+
- Backwards compatibility
51+
- Documentation: public methods (not prefixed with _) should generally have docstrings (##) and classes should contain the @tutorial tag
52+
53+
For each of the points above, do not point out what works well, only what could be improved (if anything).
54+
Be constructive and helpful in your feedback but do not repeat yourself - only summarise potential issues.
55+
Test coverage is currently not a priority.
56+
Prefix section headers with emojis and use dividers for better readability.
57+
58+
use_sticky_comment: true
59+
60+
# Optional: Customize review based on file types
61+
# direct_prompt: |
62+
# Review this PR focusing on:
63+
# - For TypeScript files: Type safety and proper interface usage
64+
# - For API endpoints: Security, input validation, and error handling
65+
# - For React components: Performance, accessibility, and best practices
66+
# - For tests: Coverage, edge cases, and test quality
67+
68+
# Optional: Different prompts for different authors
69+
# direct_prompt: |
70+
# ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' &&
71+
# 'Welcome! Please review this PR from a first-time contributor. Be encouraging and provide detailed explanations for any suggestions.' ||
72+
# 'Please provide a thorough code review focusing on our coding standards and best practices.' }}
73+
74+
# Optional: Add specific tools for running tests or linting
75+
# allowed_tools: "Bash(npm run test),Bash(npm run lint),Bash(npm run typecheck)"
76+
77+
# Optional: Skip review for certain conditions
78+
# if: |
79+
# !contains(github.event.pull_request.title, '[skip-review]') &&
80+
# !contains(github.event.pull_request.title, '[WIP]')
81+

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Talo Godot plugin: self-hostable game dev tools
22

3-
Talo is the easiest way to add leaderboards, player authentication, socket-based multiplayer and more to your game. Using the Talo Dashboard, you can visualise and analyse your game data to make data-driven decisions.
3+
Talo is the easiest way to add leaderboards, player authentication, peer-to-peer multiplayer and more to your game. Using the Talo Dashboard, you can visualise and analyse your game data to make data-driven decisions.
44

55
## Get the plugin
66

addons/talo/apis/players_api.gd

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,33 @@ signal identification_started()
1414
## Emitted when identification fails.
1515
signal identification_failed()
1616

17+
func _handle_identify_success(alias: TaloPlayerAlias, socket_token: String = "") -> TaloPlayer:
18+
if not await Talo.is_offline():
19+
Talo.socket.reset_connection()
20+
21+
Talo.current_alias = alias
22+
23+
if not socket_token.is_empty():
24+
Talo.socket.set_socket_token(socket_token)
25+
26+
identified.emit(Talo.current_player)
27+
return Talo.current_player
28+
1729
## Identify a player using a service (e.g. "username") and identifier (e.g. "bob").
1830
func identify(service: String, identifier: String) -> TaloPlayer:
1931
identification_started.emit()
2032

33+
if await Talo.is_offline():
34+
return await identify_offline(service, identifier)
35+
2136
var res := await client.make_request(HTTPClient.METHOD_GET, "/identify?service=%s&identifier=%s" % [service, identifier])
2237
match res.status:
2338
200:
24-
Talo.socket.reset_connection()
25-
26-
Talo.current_alias = TaloPlayerAlias.new(res.body.alias)
27-
Talo.socket.set_socket_token(res.body.socketToken)
28-
identified.emit(Talo.current_player)
29-
return Talo.current_player
39+
var alias = TaloPlayerAlias.new(res.body.alias)
40+
alias.write_offline_alias()
41+
return await _handle_identify_success(alias, res.body.socketToken)
3042
_:
31-
if not await Talo.is_offline():
32-
Talo.player_auth.session_manager.clear_session()
33-
43+
Talo.player_auth.session_manager.clear_session()
3444
identification_failed.emit()
3545
return null
3646

@@ -50,6 +60,8 @@ func update() -> TaloPlayer:
5060
Talo.current_alias.player.update_from_raw_data(res.body.player)
5161
else:
5262
Talo.current_alias.player = TaloPlayer.new(res.body.player)
63+
64+
Talo.current_alias.write_offline_alias()
5365
return Talo.current_player
5466
_:
5567
return null
@@ -82,3 +94,12 @@ func generate_identifier() -> String:
8294
var size := 12
8395
var split_start := RandomNumberGenerator.new().randi_range(0, time_hash.length() - size)
8496
return time_hash.substr(split_start, size)
97+
98+
## Attempt to identify a player when they're offline
99+
func identify_offline(service: String, identifier: String) -> TaloPlayer:
100+
var offline_alias := TaloPlayerAlias.get_offline_alias()
101+
if offline_alias != null and offline_alias.matches_identify_request(service, identifier):
102+
return await _handle_identify_success(offline_alias)
103+
else:
104+
identification_failed.emit()
105+
return null

addons/talo/entities/player.gd

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ class_name TaloPlayer extends TaloEntityWithProps
44
var id: String
55
var groups: Array[TaloPlayerGroupStub] = []
66

7+
var _offline_data: Dictionary
8+
79
func _init(data: Dictionary):
810
super._init([])
911
update_from_raw_data(data)
@@ -14,6 +16,7 @@ func update_from_raw_data(data: Dictionary) -> void:
1416

1517
id = data.id
1618
groups.assign(data.groups.map(func (group): return TaloPlayerGroupStub.new(group.id, group.name)))
19+
_offline_data = data
1720

1821
## Set a property by key and value. Optionally sync the player (default true) with Talo.
1922
func set_prop(key: String, value: String, update: bool = true) -> void:
@@ -34,3 +37,7 @@ func is_in_talo_group_id(group_id: String) -> bool:
3437
## Check if the player is in a group with the given name.
3538
func is_in_talo_group_name(group_name: String) -> bool:
3639
return not groups.filter(func (group: TaloPlayerGroupStub): return group.name == group_name).is_empty()
40+
41+
## Get the offline data for the player
42+
func get_offline_data() -> Dictionary:
43+
return _offline_data

addons/talo/entities/player_alias.gd

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
class_name TaloPlayerAlias extends RefCounted
22

3+
const _OFFLINE_DATA_PATH = "user://ta.bin"
4+
35
var id: int
46
var service: String
57
var identifier: String
@@ -8,6 +10,8 @@ var last_seen_at: String
810
var created_at: String
911
var updated_at: String
1012

13+
var _offline_data: Dictionary
14+
1115
func _init(data: Dictionary):
1216
id = data.id
1317
service = data.service
@@ -16,3 +20,37 @@ func _init(data: Dictionary):
1620
last_seen_at = data.lastSeenAt
1721
created_at = data.createdAt
1822
updated_at = data.updatedAt
23+
_offline_data = data
24+
25+
## Cache the offline alias data
26+
func write_offline_alias():
27+
if Talo.settings.cache_player_on_identify:
28+
_offline_data.player = player.get_offline_data()
29+
var file := FileAccess.open_encrypted_with_pass(_OFFLINE_DATA_PATH, FileAccess.WRITE, Talo.crypto_manager.get_key())
30+
file.store_line(JSON.stringify(_offline_data))
31+
file.close()
32+
33+
## Get the offline alias data
34+
static func get_offline_alias() -> TaloPlayerAlias:
35+
if not Talo.settings.cache_player_on_identify or not FileAccess.file_exists(_OFFLINE_DATA_PATH):
36+
return null
37+
38+
var file := FileAccess.open_encrypted_with_pass(_OFFLINE_DATA_PATH, FileAccess.READ, Talo.crypto_manager.get_key())
39+
if file == null:
40+
TaloCryptoManager.handle_undecryptable_file(_OFFLINE_DATA_PATH, "offline alias file")
41+
return null
42+
43+
var json := JSON.new()
44+
json.parse(file.get_as_text())
45+
file.close()
46+
47+
return TaloPlayerAlias.new(json.data)
48+
49+
## Check if this alias matches the identify request
50+
func matches_identify_request(service: String, identifier: String) -> bool:
51+
var match = self.service == service and self.identifier == identifier
52+
if not match:
53+
var dir := DirAccess.open("user://")
54+
dir.remove(_OFFLINE_DATA_PATH)
55+
56+
return match

addons/talo/plugin.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
name="Talo Game Services"
44
description="Talo (https://trytalo.com) is an open-source game backend. Talo's Godot plugin is the easiest way to add leaderboards, player authentication, socket-based multiplayer and more to your game."
55
author="trytalo"
6-
version="0.32.0"
6+
version="0.33.0"
77
script="talo_autoload.gd"

addons/talo/samples/authentication/scripts/authentication.gd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ func _ready() -> void:
1515
_configure_signals()
1616
_make_state_visible(login)
1717

18+
# identified signal emitted before the connection were made
19+
if Talo.current_alias:
20+
_make_state_visible(in_game)
21+
1822
func _make_state_visible(state: Node2D):
1923
for child in all_states.get_children():
2024
child.visible = child == state

addons/talo/samples/authentication/scripts/in_game.gd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ signal logout_success
99

1010
func _ready() -> void:
1111
Talo.players.identified.connect(_on_player_identified)
12+
# identified signal emitted before the connection were made
13+
if Talo.current_player:
14+
_on_player_identified(Talo.current_player)
1215

1316
func _on_player_identified(player: TaloPlayer) -> void:
1417
username.text = "What would you like to do,\n%s?" % Talo.current_alias.identifier

addons/talo/samples/multiscene_saves/scripts/player_controller.gd

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ func _clear_username() -> void:
2424
func get_username() -> String:
2525
if FileAccess.file_exists(_username_file_path):
2626
var file := FileAccess.open(_username_file_path, FileAccess.READ)
27-
return file.get_as_text()
27+
var content := file.get_as_text()
28+
file.close()
29+
return content
2830
else:
2931
var file := FileAccess.open(_username_file_path, FileAccess.WRITE)
3032
var username := Talo.players.generate_identifier()
3133
file.store_string(username)
34+
file.close()
3235
return username

0 commit comments

Comments
 (0)