Optimize finalized surface artwork

This commit is contained in:
Alexander Sellite 2026-09-01 13:05:05 -04:00
parent 82938d3d57
commit f2239d248a
14 changed files with 1254 additions and 305 deletions

View file

@ -32,7 +32,7 @@ const DRAWING_RELIABLE_CHANNEL: int = 14
const FISHING_RELIABLE_CHANNEL: int = 16
const HOME_RELIABLE_CHANNEL: int = 17
const ENET_CHANNEL_COUNT: int = 18
const SURFACE_DRAWING_CAPABILITY: String = "surface_drawing_v2"
const SURFACE_DRAWING_CAPABILITY: String = "surface_drawing_v3"
const ART_SHOP_CAPABILITY: String = "art_shop_v1"
const BACKPACK_SHOP_CAPABILITY: String = "backpack_shop_v1"
const WORLD_TIME_CAPABILITY: String = "world_time_v2"

View file

@ -31,6 +31,14 @@ const ARTWORK_DIRECTORY_NAME: String = "artwork"
const STAMP_ALPHA_THRESHOLD: float = 0.5
const STAMP_COLOR_TOLERANCE: float = 0.012
const INVALID_POINTER_SCREEN_POSITION := Vector2(-1.0, -1.0)
const SNAPSHOT_CANVASES_PER_FRAME: int = 2
# A maximum-size 128 x 128 canvas consumes this entire budget, preventing
# late-join synchronization from building two large artwork textures at once.
const SNAPSHOT_GRID_CELLS_PER_FRAME: int = 16384
const FINALIZED_NODE_LOAD_DISTANCE: float = 108.0
const FINALIZED_NODE_UNLOAD_DISTANCE: float = 128.0
const FINALIZED_STREAM_INTERVAL_MSEC: int = 250
const FINALIZED_NODE_LOADS_PER_PASS: int = 1
enum GuideAction {
NONE,
@ -49,7 +57,10 @@ var _art_unlocks: PlayerArtUnlocks
var _data_root: PlayerDataRoot
var _drawing_root: Node3D
var _canvas_states: Dictionary[String, Dictionary] = {}
var _canvas_cells: Dictionary[String, SurfaceDrawingCellBuffer] = {}
var _canvas_nodes: Dictionary[String, SurfaceDrawingCanvas] = {}
var _snapshot_queues: Dictionary[int, Array] = {}
var _last_finalized_stream_msec: int = 0
var _selected_canvas_id: String = ""
var _hovered_canvas_id: String = ""
var _last_hovered_canvas_id: String = ""
@ -753,8 +764,8 @@ func get_canvas_count() -> int:
func get_painted_cell_count() -> int:
var count: int = 0
for state: Dictionary in _canvas_states.values():
count += (state.get("cells", []) as Array).size()
for buffer: SurfaceDrawingCellBuffer in _canvas_cells.values():
count += buffer.get_painted_count()
return count
@ -807,7 +818,13 @@ func get_canvas_ids() -> Array[String]:
func get_canvas_state(canvas_id: String) -> Dictionary:
return Dictionary(_canvas_states.get(canvas_id, {})).duplicate(true)
var state: Dictionary = _canvas_states.get(canvas_id, {})
var buffer: SurfaceDrawingCellBuffer = _canvas_cells.get(canvas_id)
if state.is_empty() or buffer == null:
return {}
var result: Dictionary = state.duplicate(true)
result["cells"] = buffer.to_cells()
return result
func is_stamp_mode() -> bool:
@ -1029,6 +1046,8 @@ func _next_artwork_export_path(directory: String) -> String:
func _process(_delta: float) -> void:
_pump_snapshot_queues()
_refresh_finalized_canvas_streaming()
if not _active:
return
if not can_activate():
@ -1313,8 +1332,8 @@ func _handle_canvas_request(peer_id: int, data: Dictionary) -> void:
or not _peer_can_place_grid(peer_id, requested_size)
or _canvas_states.size() >= SurfaceDrawingProtocol.MAX_CANVASES
or _active_canvas_count() >= SurfaceDrawingProtocol.MAX_ACTIVE_CANVASES
or _allocated_grid_cells() + requested_size * requested_size
> SurfaceDrawingProtocol.MAX_SESSION_GRID_CELLS
or _allocated_active_grid_cells() + requested_size * requested_size
> SurfaceDrawingProtocol.MAX_ACTIVE_GRID_CELLS
):
return
var avatar: Player = _spawn_service.get_avatar(peer_id)
@ -1400,8 +1419,10 @@ func _handle_stamp_request(peer_id: int, data: Dictionary) -> void:
or not _accept_request(peer_id, str(data["request_id"]))
or not _peer_can_stamp(peer_id, data)
or _canvas_states.size() >= SurfaceDrawingProtocol.MAX_CANVASES
or _allocated_grid_cells() + requested_size * requested_size
> SurfaceDrawingProtocol.MAX_SESSION_GRID_CELLS
or _finalized_canvas_count()
>= SurfaceDrawingProtocol.MAX_FINALIZED_CANVASES
or _allocated_finalized_grid_cells() + requested_size * requested_size
> SurfaceDrawingProtocol.MAX_FINALIZED_GRID_CELLS
):
return
var avatar: Player = _spawn_service.get_avatar(peer_id)
@ -1442,6 +1463,12 @@ func _handle_stamp_request(peer_id: int, data: Dictionary) -> void:
var canvas_id: String = "%s-%d" % [
_session.get_session_id().left(16), _canvas_sequence,
]
var stamp_colors: PackedByteArray = data["pixels"]
var stamp_authors := PackedInt32Array()
stamp_authors.resize(stamp_colors.size())
for cell_key: int in stamp_colors.size():
if stamp_colors[cell_key] > 0:
stamp_authors[cell_key] = 1
var state: Dictionary = {
"session_id": _session.get_session_id(),
"canvas_id": canvas_id,
@ -1459,9 +1486,9 @@ func _handle_stamp_request(peer_id: int, data: Dictionary) -> void:
"layer": _canvas_sequence,
"creator_fingerprint": record.identity_fingerprint,
"participant_fingerprints": [record.identity_fingerprint],
"cells": _stamp_cells(
data["pixels"], requested_size, record.identity_fingerprint
),
"cell_encoding": SurfaceDrawingProtocol.CELL_ENCODING,
"cell_colors": stamp_colors,
"cell_authors": stamp_authors,
"stamp": true,
}
_apply_canvas_state(state)
@ -1469,27 +1496,6 @@ func _handle_stamp_request(peer_id: int, data: Dictionary) -> void:
_emit_hud_state("stamp placed • finalized artwork shared with everyone")
func _stamp_cells(
pixels: PackedByteArray,
grid_size: int,
author_fingerprint: String,
) -> Array[Dictionary]:
var result: Array[Dictionary] = []
var palette_ids: Array[StringName] = SurfaceDrawingPalette.get_color_ids()
for cell_key: int in range(pixels.size()):
var palette_index: int = pixels[cell_key]
if palette_index <= 0:
continue
var cell_y: int = floori(float(cell_key) / float(grid_size))
result.append({
"x": cell_key % grid_size,
"y": cell_y,
"color_id": str(palette_ids[palette_index - 1]),
"author_fingerprint": author_fingerprint,
})
return result
@rpc(
"any_peer",
"call_remote",
@ -1529,6 +1535,18 @@ func _handle_guide_request(peer_id: int, data: Dictionary) -> void:
var finalized: bool = bool(data["finalized"])
if was_finalized:
return
if (
finalized
and (
_finalized_canvas_count()
>= SurfaceDrawingProtocol.MAX_FINALIZED_CANVASES
or _allocated_finalized_grid_cells()
+ int(state["width"]) * int(state["height"])
> SurfaceDrawingProtocol.MAX_FINALIZED_GRID_CELLS
)
):
_emit_hud_state("finished artwork limit reached for this session")
return
var guide_visible: bool = bool(data["guide_visible"]) and not finalized
if (
bool(state.get("guide_visible", true)) == guide_visible
@ -1774,18 +1792,23 @@ func _queue_overlapping_finished_mutations(
).normalized()
if normal.dot(target_normal) < 0.9:
continue
var canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
if (
canvas == null
or not canvas.contains_world_point(
not _state_contains_world_point(
state,
world_position,
SurfaceDrawingCanvas.SURFACE_SAMPLE_DEPTH + 0.05,
)
):
continue
var center_cell: Vector2i = canvas.cell_at_world_point(world_position)
var center_cell: Vector2i = _state_cell_at_world_point(
state, world_position
)
if center_cell.x < 0:
continue
var tangent: Vector3 = SurfaceDrawingProtocol.array_to_vector(
state["tangent"]
).normalized()
var bitangent: Vector3 = normal.cross(tangent).normalized()
for y_offset: int in range(-1, 2):
for x_offset: int in range(-1, 2):
var cell := Vector2i(
@ -1794,19 +1817,19 @@ func _queue_overlapping_finished_mutations(
)
if (
cell.x < 0
or cell.x >= canvas.grid_width
or cell.x >= int(state["width"])
or cell.y < 0
or cell.y >= canvas.grid_height
or cell.y >= int(state["height"])
):
continue
var relative: Vector3 = (
canvas.get_cell_world_position(cell.x, cell.y)
_state_cell_position(state, cell.x, cell.y)
- world_position
)
if (
absf(relative.dot(canvas.get_surface_tangent()))
absf(relative.dot(tangent))
>= SurfaceDrawingProtocol.CELL_SIZE
or absf(relative.dot(canvas.get_surface_bitangent()))
or absf(relative.dot(bitangent))
>= SurfaceDrawingProtocol.CELL_SIZE
or _state_cell(state, cell.x, cell.y).is_empty()
):
@ -1861,11 +1884,9 @@ func _publish_cell_mutations(
canvas_ids.sort()
for canvas_id: String in canvas_ids:
var state: Dictionary = _canvas_states.get(canvas_id, {})
if state.is_empty():
var buffer: SurfaceDrawingCellBuffer = _canvas_cells.get(canvas_id)
if state.is_empty() or buffer == null:
continue
var cells: Dictionary[int, Dictionary] = _cells_by_key(
state["cells"], int(state["width"])
)
var canvas_mutations: Dictionary = mutations[canvas_id]
var cell_keys: Array[int] = []
for cell_key_value: Variant in canvas_mutations.keys():
@ -1874,18 +1895,11 @@ func _publish_cell_mutations(
var edits: Array[Dictionary] = []
for cell_key: int in cell_keys:
var edit: Dictionary = canvas_mutations[cell_key]
if str(edit["color_id"]).is_empty():
cells.erase(cell_key)
else:
cells[cell_key] = edit.duplicate(true)
edits.append(edit.duplicate(true))
if edits.is_empty():
if edits.is_empty() or not buffer.apply_edits(edits):
continue
state["revision"] = int(state["revision"]) + 1
state["cells"] = _sorted_cells(cells)
state["participant_fingerprints"] = _merged_participants(
state, {"edits": edits}
)
state["participant_fingerprints"] = buffer.get_participants()
_canvas_states[canvas_id] = state
var update: Dictionary = {
"session_id": _session.get_session_id(),
@ -1898,7 +1912,7 @@ func _publish_cell_mutations(
}
var local_canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
if local_canvas != null:
local_canvas.apply_update(update)
local_canvas.apply_buffered_update(update)
_broadcast_canvas_update(update)
_emit_artwork_changed()
@ -2068,8 +2082,13 @@ func request_canvas_snapshot(request_id: String) -> void:
)
):
return
for state: Dictionary in _canvas_states.values():
receive_canvas_state.rpc_id(sender_id, state)
var canvas_ids: Array[String] = get_canvas_ids()
canvas_ids.sort_custom(func(first: String, second: String) -> bool:
return _snapshot_canvas_precedes(first, second, sender_id)
)
var queued: Array = []
queued.assign(canvas_ids)
_snapshot_queues[sender_id] = queued
func _apply_canvas_state(data: Dictionary) -> void:
@ -2085,12 +2104,21 @@ func _apply_canvas_state(data: Dictionary) -> void:
and int(data["revision"]) <= int(previous_state["revision"])
):
return
var buffer := SurfaceDrawingCellBuffer.new()
if not buffer.configure_from_state(data, true):
return
var normalized_state: Dictionary = data.duplicate(true)
normalized_state["participant_fingerprints"] = _state_participants(
normalized_state
)
normalized_state.erase("cells")
normalized_state.erase("cell_encoding")
normalized_state.erase("cell_colors")
normalized_state.erase("cell_authors")
normalized_state["participant_fingerprints"] = buffer.get_participants()
_canvas_states[canvas_id] = normalized_state
_rebuild_canvas_node(normalized_state)
_canvas_cells[canvas_id] = buffer
if _should_load_canvas_node(normalized_state, FINALIZED_NODE_LOAD_DISTANCE):
_rebuild_canvas_node(normalized_state, buffer)
else:
_unload_canvas_node(canvas_id)
_refresh_stencil_visibility()
_emit_artwork_changed()
if (
@ -2105,7 +2133,10 @@ func _apply_canvas_state(data: Dictionary) -> void:
)
func _rebuild_canvas_node(data: Dictionary) -> void:
func _rebuild_canvas_node(
data: Dictionary,
buffer: SurfaceDrawingCellBuffer,
) -> void:
var canvas_id: String = str(data["canvas_id"])
var previous_canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
if previous_canvas != null:
@ -2113,12 +2144,82 @@ func _rebuild_canvas_node(data: Dictionary) -> void:
var canvas := SurfaceDrawingCanvas.new()
canvas.name = "Drawing_%s" % canvas_id
_drawing_root.add_child(canvas)
if not canvas.setup(data, _relationships, SOLID_SURFACE_MASK):
if not canvas.setup(data, _relationships, SOLID_SURFACE_MASK, buffer):
canvas.queue_free()
return
_canvas_nodes[canvas_id] = canvas
func _refresh_finalized_canvas_streaming() -> void:
var now: int = Time.get_ticks_msec()
if now - _last_finalized_stream_msec < FINALIZED_STREAM_INTERVAL_MSEC:
return
_last_finalized_stream_msec = now
for canvas_id: String in _canvas_nodes.keys():
var state: Dictionary = _canvas_states.get(canvas_id, {})
if (
bool(state.get("finalized", false))
and not _should_load_canvas_node(
state, FINALIZED_NODE_UNLOAD_DISTANCE
)
):
_unload_canvas_node(canvas_id)
var candidates: Array[String] = []
for canvas_id: String in _canvas_states:
var state: Dictionary = _canvas_states[canvas_id]
if (
not _canvas_nodes.has(canvas_id)
and _should_load_canvas_node(state, FINALIZED_NODE_LOAD_DISTANCE)
):
candidates.append(canvas_id)
candidates.sort_custom(func(first: String, second: String) -> bool:
return _canvas_distance_squared(first) < _canvas_distance_squared(second)
)
for index: int in mini(candidates.size(), FINALIZED_NODE_LOADS_PER_PASS):
var canvas_id: String = candidates[index]
var buffer: SurfaceDrawingCellBuffer = _canvas_cells.get(canvas_id)
if buffer != null:
_rebuild_canvas_node(_canvas_states[canvas_id], buffer)
func _should_load_canvas_node(state: Dictionary, distance: float) -> bool:
if state.is_empty():
return false
if not bool(state.get("finalized", false)):
return true
if _local_player == null or not is_instance_valid(_local_player):
return false
return _local_player.global_position.distance_squared_to(
SurfaceDrawingProtocol.array_to_vector(state["origin"])
) <= distance * distance
func _canvas_distance_squared(canvas_id: String) -> float:
var state: Dictionary = _canvas_states.get(canvas_id, {})
if (
state.is_empty()
or _local_player == null
or not is_instance_valid(_local_player)
):
return INF
return _local_player.global_position.distance_squared_to(
SurfaceDrawingProtocol.array_to_vector(state["origin"])
)
func _unload_canvas_node(canvas_id: String) -> void:
var canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
if canvas != null:
canvas.queue_free()
_canvas_nodes.erase(canvas_id)
if _selected_canvas_id == canvas_id:
_selected_canvas_id = ""
if _hovered_canvas_id == canvas_id:
_hovered_canvas_id = ""
if _last_hovered_canvas_id == canvas_id:
_last_hovered_canvas_id = ""
func _apply_canvas_update(data: Dictionary) -> void:
if (
not SurfaceDrawingProtocol.validate_canvas_update(data)
@ -2127,12 +2228,14 @@ func _apply_canvas_update(data: Dictionary) -> void:
return
var canvas_id: String = str(data["canvas_id"])
var state: Dictionary = _canvas_states.get(canvas_id, {})
var buffer: SurfaceDrawingCellBuffer = _canvas_cells.get(canvas_id)
var canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
if state.is_empty() or canvas == null or int(data["revision"]) <= int(state["revision"]):
if (
state.is_empty()
or buffer == null
or int(data["revision"]) <= int(state["revision"])
):
return
var cells: Dictionary[int, Dictionary] = _cells_by_key(
state["cells"], int(state["width"])
)
var grid_width: int = int(state["width"])
var grid_height: int = int(state["height"])
for edit_value: Variant in data["edits"]:
@ -2141,18 +2244,13 @@ func _apply_canvas_update(data: Dictionary) -> void:
var y: int = int(edit["y"])
if x < 0 or x >= grid_width or y < 0 or y >= grid_height:
return
var key: int = y * grid_width + x
if str(edit["color_id"]).is_empty():
cells.erase(key)
else:
cells[key] = edit.duplicate(true)
if not buffer.apply_edits(data["edits"]):
return
state["revision"] = int(data["revision"])
state["cells"] = _sorted_cells(cells)
state["participant_fingerprints"] = _merged_participants(
state, data
)
state["participant_fingerprints"] = buffer.get_participants()
_canvas_states[canvas_id] = state
canvas.apply_update(data)
if canvas != null:
canvas.apply_buffered_update(data)
_emit_artwork_changed()
@ -2167,7 +2265,6 @@ func _apply_guide_update(data: Dictionary) -> void:
var canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
if (
state.is_empty()
or canvas == null
or int(data["revision"]) <= int(state["revision"])
):
return
@ -2179,8 +2276,13 @@ func _apply_guide_update(data: Dictionary) -> void:
state["participant_fingerprints"] = _merged_participants(
state, data
)
var buffer: SurfaceDrawingCellBuffer = _canvas_cells.get(canvas_id)
if buffer != null:
buffer.ensure_participants(state["participant_fingerprints"])
state["participant_fingerprints"] = buffer.get_participants()
_canvas_states[canvas_id] = state
canvas.apply_guide_update(data)
if canvas != null:
canvas.apply_guide_update(data)
if _active:
_emit_hud_state(
"grid finalized • new grids may overlap its artwork"
@ -2193,13 +2295,90 @@ func _apply_guide_update(data: Dictionary) -> void:
func _broadcast_canvas_state(data: Dictionary) -> void:
var network_state: Dictionary = _network_canvas_state(
str(data.get("canvas_id", ""))
)
if network_state.is_empty():
return
for peer_id: int in _session.get_authenticated_peer_ids():
if peer_id == _session.get_local_peer_id():
continue
if _session.peer_supports_capability(
peer_id, SurfaceDrawingProtocol.CAPABILITY
):
receive_canvas_state.rpc_id(peer_id, data)
receive_canvas_state.rpc_id(peer_id, network_state)
func _network_canvas_state(canvas_id: String) -> Dictionary:
var state: Dictionary = _canvas_states.get(canvas_id, {})
var buffer: SurfaceDrawingCellBuffer = _canvas_cells.get(canvas_id)
if state.is_empty() or buffer == null:
return {}
var result: Dictionary = state.duplicate(true)
result.merge(buffer.to_compact_fields(), true)
return result
func _pump_snapshot_queues() -> void:
if _session == null or not _session.is_host() or _snapshot_queues.is_empty():
return
var peer_ids: Array[int] = []
peer_ids.assign(_snapshot_queues.keys())
peer_ids.sort()
for peer_id: int in peer_ids:
if not _session.is_authenticated_peer(peer_id):
_snapshot_queues.erase(peer_id)
continue
var queue: Array = _snapshot_queues.get(peer_id, [])
var sent_count: int = 0
var sent_grid_cells: int = 0
while not queue.is_empty() and sent_count < SNAPSHOT_CANVASES_PER_FRAME:
var canvas_id: String = str(queue.front())
var state: Dictionary = _canvas_states.get(canvas_id, {})
if state.is_empty():
queue.pop_front()
continue
var grid_cells: int = int(state["width"]) * int(state["height"])
if (
sent_count > 0
and sent_grid_cells + grid_cells > SNAPSHOT_GRID_CELLS_PER_FRAME
):
break
queue.pop_front()
var network_state: Dictionary = _network_canvas_state(canvas_id)
if network_state.is_empty():
continue
receive_canvas_state.rpc_id(peer_id, network_state)
sent_count += 1
sent_grid_cells += grid_cells
if queue.is_empty():
_snapshot_queues.erase(peer_id)
else:
_snapshot_queues[peer_id] = queue
func _snapshot_canvas_precedes(
first_id: String,
second_id: String,
peer_id: int,
) -> bool:
var first: Dictionary = _canvas_states.get(first_id, {})
var second: Dictionary = _canvas_states.get(second_id, {})
var first_finalized: bool = bool(first.get("finalized", false))
var second_finalized: bool = bool(second.get("finalized", false))
if first_finalized != second_finalized:
return not first_finalized
var avatar: Player = _spawn_service.get_avatar(peer_id)
if avatar != null:
var first_distance: float = avatar.global_position.distance_squared_to(
SurfaceDrawingProtocol.array_to_vector(first.get("origin", []))
)
var second_distance: float = avatar.global_position.distance_squared_to(
SurfaceDrawingProtocol.array_to_vector(second.get("origin", []))
)
if not is_equal_approx(first_distance, second_distance):
return first_distance < second_distance
return int(first.get("layer", 0)) < int(second.get("layer", 0))
func _broadcast_canvas_update(data: Dictionary) -> void:
@ -2285,6 +2464,61 @@ func _state_cell_position(state: Dictionary, x: int, y: int) -> Vector3:
)
func _state_contains_world_point(
state: Dictionary,
world_point: Vector3,
tolerance: float = 0.3,
) -> bool:
var origin: Vector3 = SurfaceDrawingProtocol.array_to_vector(state["origin"])
var normal: Vector3 = SurfaceDrawingProtocol.array_to_vector(
state["normal"]
).normalized()
var relative: Vector3 = world_point - origin
if absf(relative.dot(normal)) > tolerance:
return false
var tangent: Vector3 = SurfaceDrawingProtocol.array_to_vector(
state["tangent"]
).normalized()
var bitangent: Vector3 = normal.cross(tangent).normalized()
var half_width: float = (
float(state["width"]) * float(state["cell_size"]) * 0.5
)
var half_height: float = (
float(state["height"]) * float(state["cell_size"]) * 0.5
)
var horizontal: float = relative.dot(tangent)
var vertical: float = relative.dot(bitangent)
return (
horizontal >= -half_width
and horizontal < half_width
and vertical >= -half_height
and vertical < half_height
)
func _state_cell_at_world_point(
state: Dictionary,
world_point: Vector3,
) -> Vector2i:
var origin: Vector3 = SurfaceDrawingProtocol.array_to_vector(state["origin"])
var normal: Vector3 = SurfaceDrawingProtocol.array_to_vector(
state["normal"]
).normalized()
var tangent: Vector3 = SurfaceDrawingProtocol.array_to_vector(
state["tangent"]
).normalized()
var bitangent: Vector3 = normal.cross(tangent).normalized()
var relative: Vector3 = world_point - origin
var size: float = float(state["cell_size"])
var half_width: float = float(state["width"]) * size * 0.5
var half_height: float = float(state["height"]) * size * 0.5
var x: int = floori((relative.dot(tangent) + half_width) / size)
var y: int = floori((relative.dot(bitangent) + half_height) / size)
if x < 0 or x >= int(state["width"]) or y < 0 or y >= int(state["height"]):
return Vector2i(-1, -1)
return Vector2i(x, y)
func _overlaps_existing_canvas(
origin: Vector3,
normal: Vector3,
@ -2335,10 +2569,27 @@ func _active_canvas_count() -> int:
return count
func _allocated_grid_cells() -> int:
func _finalized_canvas_count() -> int:
var count: int = 0
for state: Dictionary in _canvas_states.values():
count += int(state.get("width", 0)) * int(state.get("height", 0))
if bool(state.get("finalized", false)):
count += 1
return count
func _allocated_active_grid_cells() -> int:
var count: int = 0
for state: Dictionary in _canvas_states.values():
if not bool(state.get("finalized", false)):
count += int(state.get("width", 0)) * int(state.get("height", 0))
return count
func _allocated_finalized_grid_cells() -> int:
var count: int = 0
for state: Dictionary in _canvas_states.values():
if bool(state.get("finalized", false)):
count += int(state.get("width", 0)) * int(state.get("height", 0))
return count
@ -2678,30 +2929,10 @@ func _new_request_id(prefix: String) -> String:
return "%s-%d-%d" % [prefix, Time.get_ticks_msec(), _request_sequence]
func _cells_by_key(
values: Array,
grid_width: int,
) -> Dictionary[int, Dictionary]:
var result: Dictionary[int, Dictionary] = {}
for value: Variant in values:
if typeof(value) != TYPE_DICTIONARY:
continue
var cell: Dictionary = value
var key: int = (
int(cell.get("y", -1)) * grid_width
+ int(cell.get("x", -1))
)
result[key] = cell.duplicate(true)
return result
func _state_cell(state: Dictionary, x: int, y: int) -> Dictionary:
var cells: Dictionary[int, Dictionary] = _cells_by_key(
state.get("cells", []), int(state["width"])
)
return Dictionary(
cells.get(_cell_key_for_state(state, x, y), {})
).duplicate(true)
var canvas_id: String = str(state.get("canvas_id", ""))
var buffer: SurfaceDrawingCellBuffer = _canvas_cells.get(canvas_id)
return buffer.get_cell_value(x, y) if buffer != null else {}
func _register_canvas_participant(
@ -2721,6 +2952,9 @@ func _register_canvas_participant(
participants.append(fingerprint)
state["participant_fingerprints"] = participants
_canvas_states[canvas_id] = state
var buffer: SurfaceDrawingCellBuffer = _canvas_cells.get(canvas_id)
if buffer != null:
buffer.ensure_participants(participants)
return true
@ -2740,17 +2974,17 @@ func _merged_participants(
func _state_participants(state: Dictionary) -> Array[String]:
var buffer: SurfaceDrawingCellBuffer = _canvas_cells.get(
str(state.get("canvas_id", ""))
)
if buffer != null:
return buffer.get_participants()
var result: Array[String] = []
_append_participant(
result, str(state.get("creator_fingerprint", ""))
)
for value: Variant in state.get("participant_fingerprints", []):
_append_participant(result, str(value))
for cell_value: Variant in state.get("cells", []):
var cell: Dictionary = cell_value
_append_participant(
result, str(cell.get("author_fingerprint", ""))
)
return result
@ -2804,15 +3038,6 @@ func _mutation_key(canvas_id: String, x: int, y: int) -> String:
return "%s:%d:%d" % [canvas_id, x, y]
func _sorted_cells(values: Dictionary[int, Dictionary]) -> Array[Dictionary]:
var keys: Array[int] = values.keys()
keys.sort()
var result: Array[Dictionary] = []
for key: int in keys:
result.append(values[key].duplicate(true))
return result
func _normalized_or(value: Variant, fallback: Vector3) -> Vector3:
var vector: Vector3 = value if typeof(value) == TYPE_VECTOR3 else fallback
return fallback if vector.is_zero_approx() else vector.normalized()
@ -2850,6 +3075,7 @@ func _on_peer_removed(peer_id: int) -> void:
_peer_request_ids.erase(peer_id)
_stroke_history_by_peer.erase(peer_id)
_peer_art_entitlements.erase(peer_id)
_snapshot_queues.erase(peer_id)
func _on_local_art_entitlement_changed() -> void:
@ -2898,6 +3124,7 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
func _clear_session_artwork_state() -> void:
_canvas_states.clear()
_canvas_cells.clear()
for canvas: SurfaceDrawingCanvas in _canvas_nodes.values():
canvas.queue_free()
_canvas_nodes.clear()
@ -2908,6 +3135,7 @@ func _clear_session_artwork_state() -> void:
_stroke_history_by_peer.clear()
_cell_last_stroke.clear()
_last_local_stroke_id = ""
_snapshot_queues.clear()
_hide_previews()
_emit_artwork_changed()
@ -2932,16 +3160,8 @@ func _apply_artwork_fingerprint_clear(fingerprint: String) -> int:
func _remove_canvas_state(canvas_id: String) -> void:
_canvas_states.erase(canvas_id)
var canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
if canvas != null:
canvas.queue_free()
_canvas_nodes.erase(canvas_id)
if _selected_canvas_id == canvas_id:
_selected_canvas_id = ""
if _hovered_canvas_id == canvas_id:
_hovered_canvas_id = ""
if _last_hovered_canvas_id == canvas_id:
_last_hovered_canvas_id = ""
_canvas_cells.erase(canvas_id)
_unload_canvas_node(canvas_id)
func _emit_artwork_changed() -> void: