Files

146 lines
4.1 KiB
GDScript

class_name Packet
extends RefCounted
## Входящий пакет (карточка в буфере приёма).
##
## ВАЖНО: пакет не тикает сам — все таймеры двигает HourDirector.tick().
## Состояния: waiting → (lost) → connected → loading → удалён из буфера.
## `lost` — карточка UDP-потери, 1.5 с она висит и возвращается с тем же
## оставшимся временем (D03/D09).
const STATE_WAITING := "waiting" # ждёт соединения/переноса в папку
const STATE_CONNECTED := "connected" # TCP-соединение установлено
const STATE_LOADING := "loading" # идёт загрузка в папку
const STATE_LOST := "lost" # UDP-потеря, карточка ещё 1.5 с
const UDP_LOST_SEC := 1.5
const UDP_LOST_CHANCE := 0.20
var id: String = ""
var file_name: String = ""
var type: String = "text"
var protocol: String = "tcp"
var time_left: float = 0.0
var timer_total: float = 0.0
var state: String = STATE_WAITING
var loading_progress: float = 0.0
var loading_folder_id: String = ""
var is_attack: bool = false
var attack_kind: String = ""
var size_kb: int = 0
## Служебные поля (не входят в минимальный контракт, но нужны директору).
var lost_left: float = 0.0
var loading_duration: float = 0.0
var order: int = 0
var attack_id: String = ""
func is_tcp() -> bool:
return protocol.to_lower() == "tcp"
func is_udp() -> bool:
return protocol.to_lower() == "udp"
func in_buffer() -> bool:
return state == STATE_WAITING or state == STATE_CONNECTED or state == STATE_LOST or state == STATE_LOADING
func can_connect() -> bool:
return is_tcp() and state == STATE_WAITING
func can_receive() -> bool:
return state == STATE_WAITING or state == STATE_CONNECTED
func is_loading() -> bool:
return state == STATE_LOADING
func time_ratio() -> float:
if timer_total <= 0.0:
return 0.0
return clampf(time_left / timer_total, 0.0, 1.0)
func display_name() -> String:
if is_attack and attack_kind == "ransomware":
return file_name
var ext := _ext_for_type()
if ext.is_empty():
return file_name
return "%s.%s" % [file_name, ext]
func to_dict() -> Dictionary:
return {
"id": id,
"file_name": file_name,
"type": type,
"protocol": protocol,
"time_left": time_left,
"timer_total": timer_total,
"state": state,
"loading_progress": loading_progress,
"loading_folder_id": loading_folder_id,
"is_attack": is_attack,
"attack_kind": attack_kind,
"size_kb": size_kb,
"lost_left": lost_left,
"loading_duration": loading_duration,
"order": order,
"attack_id": attack_id,
}
static func from_dict(d: Dictionary):
var p = new()
p.id = str(d.get("id", ""))
p.file_name = str(d.get("file_name", ""))
p.type = str(d.get("type", "text"))
p.protocol = str(d.get("protocol", "tcp")).to_lower()
p.time_left = float(d.get("time_left", 0.0))
p.timer_total = float(d.get("timer_total", 0.0))
p.state = str(d.get("state", STATE_WAITING))
if p.state != STATE_WAITING and p.state != STATE_CONNECTED and p.state != STATE_LOADING and p.state != STATE_LOST:
p.state = STATE_WAITING
p.loading_progress = float(d.get("loading_progress", 0.0))
p.loading_folder_id = str(d.get("loading_folder_id", ""))
p.is_attack = bool(d.get("is_attack", false))
p.attack_kind = str(d.get("attack_kind", ""))
p.size_kb = int(d.get("size_kb", 0))
p.lost_left = float(d.get("lost_left", 0.0))
p.loading_duration = float(d.get("loading_duration", 0.0))
p.order = int(d.get("order", 0))
p.attack_id = str(d.get("attack_id", ""))
return p
func _ext_for_type() -> String:
var data = _data_node()
if data != null and data.has_method("type_def"):
var def = data.type_def(type)
if def is Dictionary:
var ext := str(def.get("ext", "")).lstrip(".")
if not ext.is_empty():
return ext
match type:
"text":
return "txt"
"sound":
return "wav"
"photo":
return "jpg"
"video":
return "mp4"
return ""
func _data_node():
var loop := Engine.get_main_loop()
if loop is SceneTree:
return (loop as SceneTree).root.get_node_or_null("Data")
return null