netfishing/network/network_sale_protocol.gd

122 lines
3.8 KiB
GDScript

class_name NetworkSaleProtocol
extends RefCounted
const CAPABILITY: StringName = &"sale_v1"
const RELIABLE_CHANNEL: int = NetworkProtocol.SALE_RELIABLE_CHANNEL
const MAX_ID_LENGTH: int = 96
const MAX_CATCH_ID_LENGTH: int = 160
const MAX_CATCHES_PER_REQUEST: int = 64
const MAX_MESSAGE_LENGTH: int = 160
enum Rejection {
NONE,
MALFORMED,
STALE_SESSION,
UNAUTHENTICATED,
ALREADY_PENDING,
BUYER_UNAVAILABLE,
TOO_FAR,
INVALID_CATCH,
FAVORITED,
UNSUPPORTED,
}
static func validate_request(data: Variant) -> String:
if typeof(data) != TYPE_DICTIONARY:
return "Sale could not be completed."
var payload: Dictionary = data
for key: String in ["request_id", "session_id", "buyer_id", "catches"]:
if not payload.has(key):
return "Sale could not be completed."
if (
typeof(payload["request_id"]) != TYPE_STRING
or typeof(payload["session_id"]) != TYPE_STRING
or typeof(payload["buyer_id"]) not in [TYPE_STRING, TYPE_STRING_NAME]
or typeof(payload["catches"]) != TYPE_ARRAY
):
return "Sale could not be completed."
var request_id: String = payload["request_id"]
var session_id: String = payload["session_id"]
var buyer_id: String = str(payload["buyer_id"])
var catches: Array = payload["catches"]
if (
request_id.is_empty()
or request_id.length() > MAX_ID_LENGTH
or session_id.is_empty()
or session_id.length() > MAX_ID_LENGTH
or buyer_id.is_empty()
or buyer_id.length() > MAX_ID_LENGTH
or catches.is_empty()
or catches.size() > MAX_CATCHES_PER_REQUEST
):
return "Sale could not be completed."
var seen: Dictionary[String, bool] = {}
for value: Variant in catches:
if typeof(value) != TYPE_DICTIONARY:
return "Sale could not be completed."
var evidence: Dictionary = value
for key: String in [
"catch_id", "fish_id", "weight_lb", "display_scale",
"sale_value", "is_favorited",
]:
if not evidence.has(key):
return "Sale could not be completed."
var catch_id: String = str(evidence["catch_id"])
if (
catch_id.is_empty()
or catch_id.length() > MAX_CATCH_ID_LENGTH
or seen.has(catch_id)
or typeof(evidence["fish_id"]) not in [
TYPE_STRING, TYPE_STRING_NAME
]
or typeof(evidence["is_favorited"]) != TYPE_BOOL
):
return "Sale could not be completed."
seen[catch_id] = true
return ""
static func validate_result(data: Variant) -> bool:
if typeof(data) != TYPE_DICTIONARY:
return false
var payload: Dictionary = data
if not (
typeof(payload.get("result_id")) == TYPE_STRING
and not str(payload["result_id"]).is_empty()
and str(payload["result_id"]).length() <= MAX_ID_LENGTH
and typeof(payload.get("request_id")) == TYPE_STRING
and not str(payload["request_id"]).is_empty()
and str(payload["request_id"]).length() <= MAX_ID_LENGTH
and typeof(payload.get("session_id")) == TYPE_STRING
and typeof(payload.get("target_peer_id")) == TYPE_INT
and typeof(payload.get("accepted")) == TYPE_BOOL
and typeof(payload.get("catch_ids")) == TYPE_ARRAY
and payload["catch_ids"].size() <= MAX_CATCHES_PER_REQUEST
and typeof(payload.get("payout")) == TYPE_INT
and int(payload["payout"]) >= 0
and typeof(payload.get("base_value")) == TYPE_INT
and int(payload["base_value"]) >= 0
and typeof(payload.get("message")) == TYPE_STRING
and str(payload["message"]).length() <= MAX_MESSAGE_LENGTH
):
return false
if payload.has("buyer_id") and (
typeof(payload["buyer_id"]) not in [TYPE_STRING, TYPE_STRING_NAME]
or str(payload["buyer_id"]).is_empty()
or str(payload["buyer_id"]).length() > MAX_ID_LENGTH
):
return false
var seen: Dictionary[String, bool] = {}
for value: Variant in payload["catch_ids"]:
if typeof(value) not in [TYPE_STRING, TYPE_STRING_NAME]:
return false
var catch_id: String = str(value)
if (
catch_id.is_empty()
or catch_id.length() > MAX_CATCH_ID_LENGTH
or seen.has(catch_id)
):
return false
seen[catch_id] = true
return true