-module(lily@server). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/lily/server.gleam"). -export([connect/3, disconnect/2, incoming/3, start/2]). -export_type([server_event/1, server_state/2]). -opaque server_event(GPF) :: {client_connected, binary(), gleam@erlang@process:subject(binary())} | {client_disconnected, binary()} | {incoming, binary(), binary()} | {gleam_phantom, GPF}. -type server_state(GPG, GPH) :: {server_state, lily@store:store(GPG, GPH), gleam@dict:dict(binary(), gleam@erlang@process:subject(binary())), integer(), lily@protocol:serialiser(GPG, GPH)}. -file("src/lily/server.gleam", 28). -spec connect( gleam@erlang@process:subject(server_event(any())), binary(), gleam@erlang@process:subject(binary()) ) -> nil. connect(Server, Client_id, Subject) -> gleam@otp@actor:send(Server, {client_connected, Client_id, Subject}). -file("src/lily/server.gleam", 37). -spec disconnect(gleam@erlang@process:subject(server_event(any())), binary()) -> nil. disconnect(Server, Client_id) -> gleam@otp@actor:send(Server, {client_disconnected, Client_id}). -file("src/lily/server.gleam", 45). -spec incoming( gleam@erlang@process:subject(server_event(any())), binary(), binary() ) -> nil. incoming(Server, Client_id, Text) -> gleam@otp@actor:send(Server, {incoming, Client_id, Text}). -file("src/lily/server.gleam", 82). -spec broadcast_except( gleam@dict:dict(binary(), gleam@erlang@process:subject(binary())), binary(), binary() ) -> nil. broadcast_except(Clients, Message, Excluded_id) -> gleam@dict:each(Clients, fun(Id, Subject) -> case Id =:= Excluded_id of true -> nil; false -> gleam@erlang@process:send(Subject, Message) end end). -file("src/lily/server.gleam", 96). -spec handle_client_connected( server_state(GQF, GQG), binary(), gleam@erlang@process:subject(binary()) ) -> gleam@otp@actor:next(server_state(GQF, GQG), server_event(GQG)). handle_client_connected(State, Client_id, Subject) -> Clients = gleam@dict:insert(erlang:element(3, State), Client_id, Subject), gleam@otp@actor:continue( {server_state, erlang:element(2, State), Clients, erlang:element(4, State), erlang:element(5, State)} ). -file("src/lily/server.gleam", 106). -spec handle_client_disconnected(server_state(GQP, GQQ), binary()) -> gleam@otp@actor:next(server_state(GQP, GQQ), server_event(GQQ)). handle_client_disconnected(State, Client_id) -> Clients = gleam@dict:delete(erlang:element(3, State), Client_id), gleam@otp@actor:continue( {server_state, erlang:element(2, State), Clients, erlang:element(4, State), erlang:element(5, State)} ). -file("src/lily/server.gleam", 115). -spec handle_client_message(server_state(GQY, GQZ), binary(), GQZ) -> gleam@otp@actor:next(server_state(GQY, GQZ), server_event(GQZ)). handle_client_message(State, Client_id, Payload) -> Updated_store = lily@store:apply(erlang:element(2, State), Payload), New_sequence = erlang:element(4, State) + 1, Server_message = {server_message, New_sequence, Payload}, Encoded = lily@protocol:encode(Server_message, erlang:element(5, State)), broadcast_except(erlang:element(3, State), Encoded, Client_id), Acknowledge = {acknowledge, New_sequence}, Acknowledge_encoded = lily@protocol:encode( Acknowledge, erlang:element(5, State) ), case gleam_stdlib:map_get(erlang:element(3, State), Client_id) of {ok, Subject} -> gleam@erlang@process:send(Subject, Acknowledge_encoded); {error, nil} -> nil end, gleam@otp@actor:continue( {server_state, Updated_store, erlang:element(3, State), New_sequence, erlang:element(5, State)} ). -file("src/lily/server.gleam", 174). -spec handle_resync(server_state(GSA, GSB), binary(), integer()) -> gleam@otp@actor:next(server_state(GSA, GSB), server_event(GSB)). handle_resync(State, Client_id, _) -> case gleam_stdlib:map_get(erlang:element(3, State), Client_id) of {error, nil} -> gleam@otp@actor:continue(State); {ok, Subject} -> Snapshot = {snapshot, erlang:element(4, State), lily@store:get_model(erlang:element(2, State))}, Encoded = lily@protocol:encode(Snapshot, erlang:element(5, State)), gleam@erlang@process:send(Subject, Encoded), gleam@otp@actor:continue(State) end. -file("src/lily/server.gleam", 142). -spec handle_incoming(server_state(GRH, GRI), binary(), binary()) -> gleam@otp@actor:next(server_state(GRH, GRI), server_event(GRI)). handle_incoming(State, Client_id, Text) -> case lily@protocol:decode(Text, erlang:element(5, State)) of {ok, {client_message, Payload}} -> handle_client_message(State, Client_id, Payload); {ok, {resync, After_sequence}} -> handle_resync(State, Client_id, After_sequence); _ -> gleam@otp@actor:continue(State) end. -file("src/lily/server.gleam", 159). -spec handle_message(server_state(GRQ, GRR), server_event(GRR)) -> gleam@otp@actor:next(server_state(GRQ, GRR), server_event(GRR)). handle_message(State, Message) -> case Message of {client_connected, Client_id, Subject} -> handle_client_connected(State, Client_id, Subject); {client_disconnected, Client_id@1} -> handle_client_disconnected(State, Client_id@1); {incoming, Client_id@2, Text} -> handle_incoming(State, Client_id@2, Text) end. -file("src/lily/server.gleam", 54). -spec start(lily@store:store(GPS, GPT), lily@protocol:serialiser(GPS, GPT)) -> {ok, gleam@erlang@process:subject(server_event(GPT))} | {error, gleam@otp@actor:start_error()}. start(Store, Serialiser) -> Initial_state = {server_state, Store, maps:new(), 0, Serialiser}, _pipe = gleam@otp@actor:new(Initial_state), _pipe@1 = gleam@otp@actor:on_message(_pipe, fun handle_message/2), _pipe@2 = gleam@otp@actor:start(_pipe@1), gleam@result:map(_pipe@2, fun(Started) -> erlang:element(3, Started) end).