Adding log propagation and location change.

This commit is contained in:
Sergiotarxz 2023-06-18 20:29:42 +02:00
parent e2a5c3d235
commit dd944416b7
12 changed files with 273 additions and 117 deletions

View File

@ -9,19 +9,20 @@ my $build = Module::Build->new(
dist_author => 'Sergio Iglesias <contact@owlcode.tech>',
dist_abstract => 'Juego de L3TDE.',
requires => {
'Mojolicious' => 0,
'Moo' => 0,
'Params::ValidationCompiler' => 0,
'Types::Standard' => 0,
'Crypt::URandom' => 0,
'Crypt::Bcrypt' => 0,
'DBIx::Class' => 0,
'DBIx::Class::DeploymentHandler' => 0,
'UUID::URandom' => 0,
'Module::Pluggable' => 0,
'Redis' => 0,
'List::AllUtils' => 0,
'DateTime::Format::Pg' => 0,
'Mojolicious' => 0,
'Moo' => 0,
'Params::ValidationCompiler' => 0,
'Types::Standard' => 0,
'Crypt::URandom' => 0,
'Crypt::Bcrypt' => 0,
'DBIx::Class' => 0,
'DBIx::Class::DeploymentHandler' => 0,
'UUID::URandom' => 0,
'Module::Pluggable' => 0,
'Mojo::Redis' => 0,
'List::AllUtils' => 0,
'DateTime::Format::Pg' => 0,
'DateTime::Format::ISO8601::Format' => 0,
},
);
$build->create_build_script;

View File

@ -39,6 +39,7 @@ export default function Game (props: GameProps): JSX.Element {
const [currentLocation, setCurrentLocation] = React.useState<Location | null>(null)
const [connectedLocations, setConnectedLocations] = React.useState<Location[] | null>(null)
const [logLines, setLogLines] = React.useState<LogLine[] | null>(null)
const [error, setError] = React.useState<string | null>(null)
if (websocket === null) {
window.setTimeout(() => {
const locationProtocol = window.location.protocol
@ -58,7 +59,9 @@ export default function Game (props: GameProps): JSX.Element {
window.clearInterval(interval)
}, 10000)
}
const inputPackets = new InputPackets(setTeamPJs, setCurrentLocation, setConnectedLocations, setLogLines)
const inputPackets = new InputPackets(setTeamPJs,
setCurrentLocation, setConnectedLocations,
logLines, setLogLines, setError)
webSocket.onmessage = (event) => {
const packet = JSON.parse(event.data)
inputPackets.handle(packet)
@ -74,7 +77,8 @@ export default function Game (props: GameProps): JSX.Element {
<UpperPanel teamPJs={teamPJs}
currentLocation={currentLocation}
connectedLocations={connectedLocations}
logLines={logLines}/>
logLines={logLines}
websocket={websocket}/>
<BottomPanel/>
</>
)

View File

@ -3,12 +3,14 @@ import type { Location } from '@lastres/location'
import type { PJ } from '@lastres/pj'
import type { LogLine } from '@lastres/log-line'
import PJListItem from '@lastres/components/pj-list-item'
import MoveToPacket from '@lastres/output-packet/move-to'
interface UpperPanelProps {
connectedLocations: Location[] | null
teamPJs: PJ[] | null
currentLocation: Location | null
logLines: LogLine[] | null
websocket: WebSocket | null
}
interface Style {
@ -21,6 +23,7 @@ export default function UpperPanel (props: UpperPanelProps): JSX.Element {
const teamPJs = props.teamPJs
const currentLocation = props.currentLocation
const logLines = props.logLines
const websocket = props.websocket
if (!(teamPJs !== null && currentLocation !== null && connectedLocations !== null)) {
return (
<>
@ -28,14 +31,26 @@ export default function UpperPanel (props: UpperPanelProps): JSX.Element {
</>
)
}
function onClickLocation (item: Location): void {
const moveToPacket = new MoveToPacket(item)
if (websocket !== null) {
moveToPacket.send(websocket)
}
}
function generateLog (): React.ReactNode {
if (logLines === null || logLines.length < 1) {
return (
<>No log</>
)
}
return logLines.reverse().map((item, i) => {
return <>
return logLines.sort(function (a: LogLine, b: LogLine): number {
const aDate = Date.parse(a.date)
const bDate = Date.parse(b.date)
return aDate - bDate
}).map((item, i) => {
return <span key={i}>
<b>{item.date}</b> {
item.content.map((item, i) => {
const style: Style = {}
@ -48,7 +63,7 @@ export default function UpperPanel (props: UpperPanelProps): JSX.Element {
return <span key={i} style={style}>{item.text}</span>
})
} <br/>
</>
</span>
})
}
return (
@ -74,7 +89,9 @@ export default function UpperPanel (props: UpperPanelProps): JSX.Element {
{
connectedLocations.map((item, i) => {
return <li key={i}>
<a href="#">{item.area.name}/{item.location.name}</a>
<a href="#" onClick={() => {
onClickLocation(item)
}}>{item.area.name}/{item.location.name}</a>
</li>
})
}

View File

@ -4,30 +4,42 @@ import type InputPacket from '@lastres/input-packet'
import type { LogLine } from '@lastres/log-line'
import InputPacketInfo from '@lastres/input-packet/info'
import InputPacketPong from '@lastres/input-packet/pong'
type SetTeamPJs = (set: PJ[] | null) => void
type SetCurrentLocation = (set: Location | null) => void
type SetConnectedLocations = (set: Location[] | null) => void
type DispatchHash = Record<string, any>
type SetLogLines = (set: LogLine[] | null) => void
type SetLogLinesCallback = (set: LogLine[] | null) => LogLine[]
type SetLogLines = (set: LogLine[] | SetLogLinesCallback | null) => void
type SetError = (set: string | null) => void
interface Packet {
command: string
data: any
}
type LogHash = Record<string, LogLine>
export default class InputPackets {
setTeamPJs: SetTeamPJs
setCurrentLocation: SetCurrentLocation
setConnectedLocations: SetConnectedLocations
setLogLines: SetLogLines
logLines: LogLine[] | null
cachedHash: DispatchHash | null = null
cachedArray: InputPacket[] | null = null
setError: SetError
constructor (setTeamPJs: SetTeamPJs,
setCurrentLocation: SetCurrentLocation,
setConnectedLocations: SetConnectedLocations,
setLogLines: SetLogLines) {
logLines: LogLine[] | null,
setLogLines: SetLogLines,
setError: SetError) {
this.setTeamPJs = setTeamPJs
this.setCurrentLocation = setCurrentLocation
this.setConnectedLocations = setConnectedLocations
this.logLines = logLines
this.setLogLines = setLogLines
this.setError = setError
}
handle (packet: Packet): void {
@ -42,10 +54,38 @@ export default class InputPackets {
const infoPacket = new InputPacketInfo()
const pongPacket = new InputPacketPong()
infoPacket.onReceive((data) => {
this.setTeamPJs(data.team_pjs)
this.setConnectedLocations(data.location_data.connected_places)
this.setCurrentLocation(data.location_data.current)
this.setLogLines(data.set_log)
if (data.error !== undefined) {
this.setError(data.error)
return
}
if (data.team_pjs !== undefined) {
this.setTeamPJs(data.team_pjs)
}
if (data.location_data?.connected_places !== undefined) {
this.setConnectedLocations(data.location_data.connected_places)
}
if (data.location_data?.current !== undefined) {
this.setCurrentLocation(data.location_data.current)
}
if (data.set_log !== undefined) {
this.setLogLines(data.set_log)
}
if (data.append_log !== undefined) {
const logHash: LogHash = {}
this.setLogLines((logLines: LogLine[] | null): LogLine[] => {
if (logLines !== null) {
for (const item of logLines) {
logHash[item.uuid] = item
}
logHash[data.append_log.uuid] = data.append_log
const outputLog: LogLine[] = Object.keys(logHash).map((item, i): LogLine => {
return logHash[item]
})
return outputLog
}
return []
})
}
})
this.cachedArray = [infoPacket, pongPacket]
}

View File

@ -1,11 +1,10 @@
export default abstract class OutputPacket {
send(ws: WebSocket): void {
const data = this.data();
send (ws: WebSocket): void {
ws.send(JSON.stringify({
command: this.command(),
data: this.data(),
command: this.command(),
data: this.data()
}))
}
abstract data(): any
abstract command(): string
abstract data (): any
abstract command (): string
}

View File

@ -11,7 +11,7 @@ use Data::Dumper;
use Moo;
use JSON;
use JSON qw/to_json from_json/;
use LasTres::Flags;
use LasTres::Redis;
@ -29,22 +29,22 @@ sub identifier {
sub handle ( $self, $ws, $session, $data ) {
if ( ref $data ne 'HASH' ) {
return $ws->send(
encode_json( { error => "Data should be a hashref." } ) );
to_json( { error => "Data should be a hashref." } ) );
}
my $pj_uuid = $data->{pj_uuid};
if ( !defined $pj_uuid ) {
return $ws->send( encode_json( { error => "No pj sent." } ) );
return $ws->send( to_json( { error => "No pj sent." } ) );
}
my @pjs = $result_set_pjs->search( { uuid => $pj_uuid } );
if ( !scalar @pjs ) {
return $ws->send(
encode_json( { error => 'This pj does not exists' } ) );
to_json( { error => 'This pj does not exists' } ) );
}
my $pj = $pjs[0];
my $user = $session->{user};
if ( $pj->owner->uuid ne $user->uuid ) {
return $ws->send(
encode_json( { error => 'You are not the owner of this pj.' } ) );
to_json( { error => 'You are not the owner of this pj.' } ) );
}
$session->{pj} = $pj;
my $team = $pj->team;
@ -83,20 +83,34 @@ sub handle ( $self, $ws, $session, $data ) {
clear => $JSON::true,
);
$info_packet_to_send->send($ws);
require LasTres::Redis;
my $redis = LasTres::Redis->new;
say $redis->pj_subscription($pj);
$redis->subscribe($redis->pj_subscription($pj), my $save = sub($message, $topic, $topics) {
return $self->_on_redis_event($ws, $session, $message, $topic, $topics);
});
$session->{redis} = $redis;
}
sub _on_redis_event($self, $ws, $session, $message, $topic, $topics) {
say 'Receiving Redis event ' . $topic;
my $data = from_json($message);
my $pj = $session->{pj};
if ($data->{command} eq 'append-log') {
my $info_packet_to_send =
LasTres::Controller::Websocket::OutputPacket::Info->new(
append_log => $data->{log}
);
$info_packet_to_send->send($ws);
}
}
sub _get_connected_places ( $self, $pj ) {
my $team = $pj->team;
my $location = $team->location;
my $connected_places = [];
if ( $location->can('connected_places') ) {
@$connected_places = ( @{ $team->location->connected_places } );
}
@$connected_places =
( @$connected_places, @{ $location->parent->children } );
@$connected_places =
grep { $_->identifier ne $location->identifier } @$connected_places;
my $connected_places = $location->get_available_locations_to_move_to($pj);
@$connected_places = map { $_->hash } @$connected_places;
return $connected_places;
}

View File

@ -13,50 +13,45 @@ use Moo;
with 'LasTres::Controller::Websocket::OutputPacket';
has clear => (
is => 'rw',
);
has clear => ( is => 'rw', );
has team_pjs => (
is => 'rw',
);
has team_pjs => ( is => 'rw', );
has location_data => (
is => 'rw',
);
has location_data => ( is => 'rw', );
has set_log => (
is => 'rw',
);
has set_log => ( is => 'rw', );
has append_log => ( is => 'rw' );
sub identifier {
return 'info';
}
sub data($self) {
my $clear = $self->clear;
my $team_pjs = $self->team_pjs;
sub data ($self) {
my $clear = $self->clear;
my $team_pjs = $self->team_pjs;
my $location_data = $self->location_data;
my $set_log = $self->set_log;
my $set_log = $self->set_log;
my $append_log = $self->append_log;
return {
(
(defined $clear)
? (clear => $clear)
( defined $clear ) ? ( clear => $clear )
: ()
),
(
(defined $team_pjs)
? (team_pjs => $team_pjs)
( defined $team_pjs ) ? ( team_pjs => $team_pjs )
: ()
),
(
(defined $location_data)
? (location_data => $location_data)
( defined $location_data ) ? ( location_data => $location_data )
: ()
),
(
(defined $set_log)
? (set_log => [map { $_->hash } @$set_log])
( defined $set_log ) ? ( set_log => [ map { $_->hash } @$set_log ] )
: ()
),
(
( defined $append_log )
? ( append_log => $append_log )
: ()
)
};

View File

@ -10,77 +10,131 @@ use feature 'signatures';
use LasTres::Planets;
use Moo::Role;
use JSON qw/to_json/;
requires qw/identifier name description parent actions npcs/;
my $planets = LasTres::Planets->new;
sub show_intro($self, $pj) {
$pj->append_log_line([
{ text => 'Estas en ' },
{ color => 'red', text => $self->parent->name },
{ text => '/' },
{ color => 'green', text => $self->name },
]);
$pj->append_log_line([
{ text => $pj->location->description },
]);
sub show_intro ( $self, $pj ) {
$pj->append_log_line(
[
{ text => 'Estas en ' },
{ color => 'red', text => $self->parent->name },
{ text => '/' },
{ color => 'green', text => $self->name },
]
);
$pj->append_log_line( [ { text => $pj->location->description }, ] );
}
sub get($planet_id, $super_area_id, $area_id, $location_id) {
sub notify_arrival($self, $team) {
for my $pj ($team->members) {
$self->show_intro($pj);
}
}
sub place_team($self, $team) {
$team->location($self);
$team->update;
$self->notify_arrival($team);
}
sub to_json_array ($self) {
my $hash = $self->hash;
return to_json(
[
$hash->{planet}{identifier}, $hash->{super_area}{identifier},
$hash->{area}{identifier}, $hash->{location}{identifier}
]
);
}
sub is_connected_by_move ( $self, $otherLocation, $pj = undef ) {
if ( !defined $otherLocation ) {
die '$otherLocation must be defined in is_connected.';
}
my $json_array_other_location = $otherLocation->to_json_array;
if ( grep { $_->to_json_array eq $json_array_other_location }
@{ $self->get_available_locations_to_move_to($pj) } )
{
return 1;
}
return 0;
}
sub get_available_locations_to_move_to ( $self, $pj ) {
my $team = $pj->team;
my $location = $team->location;
my $connected_places = [];
if ( $location->can('connected_places') ) {
@$connected_places = ( @{ $team->location->connected_places } );
}
@$connected_places =
( @$connected_places, @{ $location->parent->children } );
@$connected_places =
grep { $_->identifier ne $location->identifier } @$connected_places;
return $connected_places;
}
sub get ( $planet_id, $super_area_id, $area_id, $location_id ) {
my $planet = $planets->hash->{$planet_id};
if (!defined $planet) {
if ( !defined $planet ) {
die "No such planet $planet_id.";
}
my $super_area = $planet->super_areas->{$super_area_id};
if (!defined $super_area) {
if ( !defined $super_area ) {
die "No such super_area $super_area_id in planet $planet_id.";
}
my $area = $super_area->areas->{$area_id};
if (!defined $area) {
die "No such area $area_id in super_area $super_area_id in planet $planet_id.";
if ( !defined $area ) {
die
"No such area $area_id in super_area $super_area_id in planet $planet_id.";
}
my $location = $area->locations->{$location_id};
if (!defined $location) {
die "No such location $location_id in area $area_id in super_area $super_area_id in planet $planet_id.";
if ( !defined $location ) {
die
"No such location $location_id in area $area_id in super_area $super_area_id in planet $planet_id.";
}
return $location;
}
sub hash($self) {
sub hash ($self) {
my $location = $self;
if (!Moo::Role::does_role($location, 'LasTres::Location')) {
if ( !Moo::Role::does_role( $location, 'LasTres::Location' ) ) {
die "$location does not implement LasTres::Location.";
}
my $area = $location->parent;
if (!Moo::Role::does_role($area, 'LasTres::Area')) {
die "$area does not implement LasTres::Area.";
if ( !Moo::Role::does_role( $area, 'LasTres::Area' ) ) {
die "$area does not implement LasTres::Area.";
}
my $super_area = $area->parent;
if (!Moo::Role::does_role($super_area, 'LasTres::SuperArea')) {
die "$super_area does not implement LasTres::SuperArea.";
if ( !Moo::Role::does_role( $super_area, 'LasTres::SuperArea' ) ) {
die "$super_area does not implement LasTres::SuperArea.";
}
my $planet = $super_area->parent;
if (!Moo::Role::does_role($planet, 'LasTres::Planet')) {
die "$planet does not implement LasTres::Planet.";
if ( !Moo::Role::does_role( $planet, 'LasTres::Planet' ) ) {
die "$planet does not implement LasTres::Planet.";
}
return {
planet => {
name => $planet->name,
name => $planet->name,
identifier => $planet->identifier,
},
super_area => {
name => $super_area->name,
name => $super_area->name,
identifier => $super_area->identifier,
},
area => {
name => $area->name,
name => $area->name,
identifier => $area->identifier,
},
location => {
name => $location->name,
name => $location->name,
identifier => $location->identifier,
},
}
};
}
1;

View File

@ -7,24 +7,37 @@ use warnings;
use feature 'signatures';
use parent 'Redis';
use parent 'Mojo::Redis';
use LasTres::Schema;
our $VERSION = $LasTres::Schema::VERSION;
{
my $cached_redis;
sub new {
my $self;
my $class = shift;
if (!defined $cached_redis) {
$cached_redis = $class->SUPER::new(@_);
if (!defined $self) {
$self = $class->SUPER::new(@_);
}
return $cached_redis;
return $self;
}
}
sub prefix {
return "LasTres::Redis::$VERSION";
}
sub subscribe($self, $topic, $callback) {
$self->pubsub->listen($topic, sub($self, $message, $topic) {
$callback->($message, $topic, [$topic]);
});
}
sub publish($self, $topic, $message) {
$self->pubsub->notify($topic, $message);
}
sub pj_subscription($class, $pj) {
return prefix() . '::Subscriptions::PJ::' . $pj->uuid;
}
1;

View File

@ -251,6 +251,8 @@ sub last_50_log ($self) {
sub append_log_line ( $self, $content ) {
require LasTres::Schema;
require LasTres::Redis;
my $redis = LasTres::Redis->new;
if ( ref $content ne 'ARRAY' ) {
die 'Bad log content, not a arrayref.';
}
@ -281,13 +283,19 @@ sub append_log_line ( $self, $content ) {
}
}
my $uuid = create_uuid_string;
LasTres::Schema->Schema->resultset('PJLog')
my $log =
LasTres::Schema->Schema->resultset('PJLog')
->new(
{ uuid => $uuid, owner => $self->uuid, content => to_json($content) } )
->insert;
{ uuid => $uuid, owner => $self->uuid, content => to_json($content) } );
$log->insert;
$log = $log->get_from_storage;
say 'Sending redis event '. $redis->pj_subscription($self);
$redis->publish( $redis->pj_subscription($self),
to_json( { command => 'append-log', log => $log->hash } ) );
}
sub location($self) {
sub location ($self) {
return $self->team->location;
}
@ -299,12 +307,12 @@ sub set_flag ( $self, $name ) {
require LasTres::Schema;
my $schema = LasTres::Schema->Schema;
my $result_set_flags = $schema->resultset('PJFlag');
my $flag = $result_set_flags->new({name => $name, owner => $self->uuid})
my $flag = $result_set_flags->new( { name => $name, owner => $self->uuid } )
->update_or_insert;
}
sub get_flag ( $self, $name ) {
my @flags = $self->flags->search({name => $name});
my @flags = $self->flags->search( { name => $name } );
if ( scalar @flags ) {
return 1;
}
@ -314,5 +322,4 @@ sub get_flag ( $self, $name ) {
sub clear_flag ( $self, $name ) {
$self->flags->search( name => $name )->delete;
}
1;

View File

@ -11,6 +11,7 @@ use parent 'DBIx::Class::Core';
use Data::Dumper;
use JSON qw/from_json/;
use DateTime::Format::ISO8601::Format;
use Moo;
@ -48,10 +49,11 @@ __PACKAGE__->set_primary_key('uuid');
__PACKAGE__->belongs_to( 'owner', 'LasTres::Schema::Result::PJ' );
sub hash ($self) {
my $iso_formatter = DateTime::Format::ISO8601::Format->new(second_precision => 3);
return {
uuid => $self->uuid,
content => $self->content,
date => $self->date,
date => $iso_formatter->format_datetime($self->date)
};
}

View File

@ -96,7 +96,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
\************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Game)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _lastres_components_upper_panel__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lastres/components/upper-panel */ \"./js-src/components/upper-panel.tsx\");\n/* harmony import */ var _lastres_components_bottom_panel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @lastres/components/bottom-panel */ \"./js-src/components/bottom-panel.tsx\");\n/* harmony import */ var _lastres_components_pj_selection_menu__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @lastres/components/pj-selection-menu */ \"./js-src/components/pj-selection-menu.tsx\");\n/* harmony import */ var _lastres_output_packet_init__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @lastres/output-packet/init */ \"./js-src/output-packet/init.ts\");\n/* harmony import */ var _lastres_output_packet_ping__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @lastres/output-packet/ping */ \"./js-src/output-packet/ping.ts\");\n/* harmony import */ var _lastres_input_packets__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @lastres/input-packets */ \"./js-src/input-packets.ts\");\n\n\n\n\n\n\n\nfunction Game(props) {\n const selectedPJ = props.selectedPJ;\n if (selectedPJ === null) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_pj_selection_menu__WEBPACK_IMPORTED_MODULE_3__[\"default\"], { setSelectedPJ: props.setSelectedPJ, userWantsToCreatePJ: props.userWantsToCreatePJ, setUserWantsToCreatePJ: props.setUserWantsToCreatePJ, error: props.error, setError: props.setError })));\n }\n const [websocket, setWebsocket] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const [teamPJs, setTeamPJs] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const [currentLocation, setCurrentLocation] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const [connectedLocations, setConnectedLocations] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const [logLines, setLogLines] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n if (websocket === null) {\n window.setTimeout(() => {\n const locationProtocol = window.location.protocol;\n if (locationProtocol == null) {\n return;\n }\n const protocol = locationProtocol.match(/https:/) != null ? 'wss' : 'ws';\n const webSocket = new WebSocket(`${protocol}://${window.location.host}/ws`);\n webSocket.onopen = () => {\n new _lastres_output_packet_init__WEBPACK_IMPORTED_MODULE_4__[\"default\"](selectedPJ.uuid).send(webSocket);\n let interval = 0;\n interval = window.setInterval(() => {\n if (webSocket.readyState === WebSocket.OPEN) {\n new _lastres_output_packet_ping__WEBPACK_IMPORTED_MODULE_5__[\"default\"]().send(webSocket);\n return;\n }\n window.clearInterval(interval);\n }, 10000);\n };\n const inputPackets = new _lastres_input_packets__WEBPACK_IMPORTED_MODULE_6__[\"default\"](setTeamPJs, setCurrentLocation, setConnectedLocations, setLogLines);\n webSocket.onmessage = (event) => {\n const packet = JSON.parse(event.data);\n inputPackets.handle(packet);\n };\n webSocket.onerror = (event) => {\n console.log(event);\n };\n setWebsocket(webSocket);\n }, 1);\n }\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_upper_panel__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { teamPJs: teamPJs, currentLocation: currentLocation, connectedLocations: connectedLocations, logLines: logLines }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_bottom_panel__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null)));\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/components/game.tsx?");
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Game)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _lastres_components_upper_panel__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lastres/components/upper-panel */ \"./js-src/components/upper-panel.tsx\");\n/* harmony import */ var _lastres_components_bottom_panel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @lastres/components/bottom-panel */ \"./js-src/components/bottom-panel.tsx\");\n/* harmony import */ var _lastres_components_pj_selection_menu__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @lastres/components/pj-selection-menu */ \"./js-src/components/pj-selection-menu.tsx\");\n/* harmony import */ var _lastres_output_packet_init__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @lastres/output-packet/init */ \"./js-src/output-packet/init.ts\");\n/* harmony import */ var _lastres_output_packet_ping__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @lastres/output-packet/ping */ \"./js-src/output-packet/ping.ts\");\n/* harmony import */ var _lastres_input_packets__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @lastres/input-packets */ \"./js-src/input-packets.ts\");\n\n\n\n\n\n\n\nfunction Game(props) {\n const selectedPJ = props.selectedPJ;\n if (selectedPJ === null) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_pj_selection_menu__WEBPACK_IMPORTED_MODULE_3__[\"default\"], { setSelectedPJ: props.setSelectedPJ, userWantsToCreatePJ: props.userWantsToCreatePJ, setUserWantsToCreatePJ: props.setUserWantsToCreatePJ, error: props.error, setError: props.setError })));\n }\n const [websocket, setWebsocket] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const [teamPJs, setTeamPJs] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const [currentLocation, setCurrentLocation] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const [connectedLocations, setConnectedLocations] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const [logLines, setLogLines] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n const [error, setError] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);\n if (websocket === null) {\n window.setTimeout(() => {\n const locationProtocol = window.location.protocol;\n if (locationProtocol == null) {\n return;\n }\n const protocol = locationProtocol.match(/https:/) != null ? 'wss' : 'ws';\n const webSocket = new WebSocket(`${protocol}://${window.location.host}/ws`);\n webSocket.onopen = () => {\n new _lastres_output_packet_init__WEBPACK_IMPORTED_MODULE_4__[\"default\"](selectedPJ.uuid).send(webSocket);\n let interval = 0;\n interval = window.setInterval(() => {\n if (webSocket.readyState === WebSocket.OPEN) {\n new _lastres_output_packet_ping__WEBPACK_IMPORTED_MODULE_5__[\"default\"]().send(webSocket);\n return;\n }\n window.clearInterval(interval);\n }, 10000);\n };\n const inputPackets = new _lastres_input_packets__WEBPACK_IMPORTED_MODULE_6__[\"default\"](setTeamPJs, setCurrentLocation, setConnectedLocations, logLines, setLogLines, setError);\n webSocket.onmessage = (event) => {\n const packet = JSON.parse(event.data);\n inputPackets.handle(packet);\n };\n webSocket.onerror = (event) => {\n console.log(event);\n };\n setWebsocket(webSocket);\n }, 1);\n }\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_upper_panel__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { teamPJs: teamPJs, currentLocation: currentLocation, connectedLocations: connectedLocations, logLines: logLines, websocket: websocket }),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_bottom_panel__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null)));\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/components/game.tsx?");
/***/ }),
@ -186,7 +186,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
\*******************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ UpperPanel)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _lastres_components_pj_list_item__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lastres/components/pj-list-item */ \"./js-src/components/pj-list-item.tsx\");\n\n\nfunction UpperPanel(props) {\n const connectedLocations = props.connectedLocations;\n const teamPJs = props.teamPJs;\n const currentLocation = props.currentLocation;\n const logLines = props.logLines;\n if (!(teamPJs !== null && currentLocation !== null && connectedLocations !== null)) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"p\", null, \"Esperando datos...\")));\n }\n function generateLog() {\n if (logLines === null || logLines.length < 1) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, \"No log\"));\n }\n return logLines.reverse().map((item, i) => {\n return react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"b\", null, item.date),\n \" \",\n item.content.map((item, i) => {\n const style = {};\n if (item.color !== undefined) {\n style.color = item.color;\n }\n if (item.background !== undefined) {\n style.background = item.background;\n }\n return react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"span\", { key: i, style: style }, item.text);\n }),\n \" \",\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"br\", null));\n });\n }\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"presentation\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"presentation-item\" }, teamPJs.map((item, i) => {\n return react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_pj_list_item__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { key: i, pj: item });\n })),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"presentation-item\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"code\", null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"pre\", null, generateLog()))),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"presentation-item\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"p\", null,\n \"Est\\u00E1s en \",\n currentLocation.area.name,\n \"/\",\n currentLocation.location.name,\n \".\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"p\", null, \"Puedes ir a:\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"ul\", null, connectedLocations.map((item, i) => {\n return react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"li\", { key: i },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { href: \"#\" },\n item.area.name,\n \"/\",\n item.location.name));\n })))));\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/components/upper-panel.tsx?");
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ UpperPanel)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _lastres_components_pj_list_item__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lastres/components/pj-list-item */ \"./js-src/components/pj-list-item.tsx\");\n/* harmony import */ var _lastres_output_packet_move_to__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @lastres/output-packet/move-to */ \"./js-src/output-packet/move-to.ts\");\n\n\n\nfunction UpperPanel(props) {\n const connectedLocations = props.connectedLocations;\n const teamPJs = props.teamPJs;\n const currentLocation = props.currentLocation;\n const logLines = props.logLines;\n const websocket = props.websocket;\n if (!(teamPJs !== null && currentLocation !== null && connectedLocations !== null)) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"p\", null, \"Esperando datos...\")));\n }\n function onClickLocation(item) {\n const moveToPacket = new _lastres_output_packet_move_to__WEBPACK_IMPORTED_MODULE_2__[\"default\"](item);\n if (websocket !== null) {\n moveToPacket.send(websocket);\n }\n }\n function generateLog() {\n if (logLines === null || logLines.length < 1) {\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, \"No log\"));\n }\n return logLines.sort(function (a, b) {\n const aDate = Date.parse(a.date);\n const bDate = Date.parse(b.date);\n return aDate - bDate;\n }).map((item, i) => {\n return react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"span\", { key: i },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"b\", null, item.date),\n \" \",\n item.content.map((item, i) => {\n const style = {};\n if (item.color !== undefined) {\n style.color = item.color;\n }\n if (item.background !== undefined) {\n style.background = item.background;\n }\n return react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"span\", { key: i, style: style }, item.text);\n }),\n \" \",\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"br\", null));\n });\n }\n return (react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"presentation\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"presentation-item\" }, teamPJs.map((item, i) => {\n return react__WEBPACK_IMPORTED_MODULE_0__.createElement(_lastres_components_pj_list_item__WEBPACK_IMPORTED_MODULE_1__[\"default\"], { key: i, pj: item });\n })),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"presentation-item\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"code\", null,\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"pre\", null, generateLog()))),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"div\", { className: \"presentation-item\" },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"p\", null,\n \"Est\\u00E1s en \",\n currentLocation.area.name,\n \"/\",\n currentLocation.location.name,\n \".\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"p\", null, \"Puedes ir a:\"),\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"ul\", null, connectedLocations.map((item, i) => {\n return react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"li\", { key: i },\n react__WEBPACK_IMPORTED_MODULE_0__.createElement(\"a\", { href: \"#\", onClick: () => {\n onClickLocation(item);\n } },\n item.area.name,\n \"/\",\n item.location.name));\n })))));\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/components/upper-panel.tsx?");
/***/ }),
@ -236,7 +236,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
\*********************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ InputPackets)\n/* harmony export */ });\n/* harmony import */ var _lastres_input_packet_info__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @lastres/input-packet/info */ \"./js-src/input-packet/info.ts\");\n/* harmony import */ var _lastres_input_packet_pong__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lastres/input-packet/pong */ \"./js-src/input-packet/pong.ts\");\n\n\nclass InputPackets {\n constructor(setTeamPJs, setCurrentLocation, setConnectedLocations, setLogLines) {\n this.cachedHash = null;\n this.cachedArray = null;\n this.setTeamPJs = setTeamPJs;\n this.setCurrentLocation = setCurrentLocation;\n this.setConnectedLocations = setConnectedLocations;\n this.setLogLines = setLogLines;\n }\n handle(packet) {\n const hash = this.hashAvailablePackets();\n const identifier = packet.command;\n const inputPacket = hash[identifier];\n inputPacket.recv(packet);\n }\n listAvailablePackets() {\n if (this.cachedArray === null) {\n const infoPacket = new _lastres_input_packet_info__WEBPACK_IMPORTED_MODULE_0__[\"default\"]();\n const pongPacket = new _lastres_input_packet_pong__WEBPACK_IMPORTED_MODULE_1__[\"default\"]();\n infoPacket.onReceive((data) => {\n this.setTeamPJs(data.team_pjs);\n this.setConnectedLocations(data.location_data.connected_places);\n this.setCurrentLocation(data.location_data.current);\n this.setLogLines(data.set_log);\n });\n this.cachedArray = [infoPacket, pongPacket];\n }\n return this.cachedArray;\n }\n hashAvailablePackets() {\n if (this.cachedHash === null) {\n this.cachedHash = {};\n for (const inputPacket of this.listAvailablePackets()) {\n this.cachedHash[inputPacket.identifier()] = inputPacket;\n }\n }\n return this.cachedHash;\n }\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/input-packets.ts?");
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ InputPackets)\n/* harmony export */ });\n/* harmony import */ var _lastres_input_packet_info__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @lastres/input-packet/info */ \"./js-src/input-packet/info.ts\");\n/* harmony import */ var _lastres_input_packet_pong__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lastres/input-packet/pong */ \"./js-src/input-packet/pong.ts\");\n\n\nclass InputPackets {\n constructor(setTeamPJs, setCurrentLocation, setConnectedLocations, logLines, setLogLines, setError) {\n this.cachedHash = null;\n this.cachedArray = null;\n this.setTeamPJs = setTeamPJs;\n this.setCurrentLocation = setCurrentLocation;\n this.setConnectedLocations = setConnectedLocations;\n this.logLines = logLines;\n this.setLogLines = setLogLines;\n this.setError = setError;\n }\n handle(packet) {\n const hash = this.hashAvailablePackets();\n const identifier = packet.command;\n const inputPacket = hash[identifier];\n inputPacket.recv(packet);\n }\n listAvailablePackets() {\n if (this.cachedArray === null) {\n const infoPacket = new _lastres_input_packet_info__WEBPACK_IMPORTED_MODULE_0__[\"default\"]();\n const pongPacket = new _lastres_input_packet_pong__WEBPACK_IMPORTED_MODULE_1__[\"default\"]();\n infoPacket.onReceive((data) => {\n if (data.error !== undefined) {\n this.setError(data.error);\n return;\n }\n if (data.team_pjs !== undefined) {\n this.setTeamPJs(data.team_pjs);\n }\n if (data.location_data?.connected_places !== undefined) {\n this.setConnectedLocations(data.location_data.connected_places);\n }\n if (data.location_data?.current !== undefined) {\n this.setCurrentLocation(data.location_data.current);\n }\n if (data.set_log !== undefined) {\n this.setLogLines(data.set_log);\n }\n if (data.append_log !== undefined) {\n const logHash = {};\n this.setLogLines((logLines) => {\n if (logLines !== null) {\n for (const item of logLines) {\n logHash[item.uuid] = item;\n }\n logHash[data.append_log.uuid] = data.append_log;\n const outputLog = Object.keys(logHash).map((item, i) => {\n return logHash[item];\n });\n return outputLog;\n }\n return [];\n });\n }\n });\n this.cachedArray = [infoPacket, pongPacket];\n }\n return this.cachedArray;\n }\n hashAvailablePackets() {\n if (this.cachedHash === null) {\n this.cachedHash = {};\n for (const inputPacket of this.listAvailablePackets()) {\n this.cachedHash[inputPacket.identifier()] = inputPacket;\n }\n }\n return this.cachedHash;\n }\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/input-packets.ts?");
/***/ }),
@ -256,7 +256,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
\*********************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ OutputPacket)\n/* harmony export */ });\nclass OutputPacket {\n send(ws) {\n const data = this.data();\n ws.send(JSON.stringify({\n command: this.command(),\n data: this.data(),\n }));\n }\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/output-packet.ts?");
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ OutputPacket)\n/* harmony export */ });\nclass OutputPacket {\n send(ws) {\n ws.send(JSON.stringify({\n command: this.command(),\n data: this.data()\n }));\n }\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/output-packet.ts?");
/***/ }),
@ -270,6 +270,16 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
/***/ }),
/***/ "./js-src/output-packet/move-to.ts":
/*!*****************************************!*\
!*** ./js-src/output-packet/move-to.ts ***!
\*****************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ MoveToPacket)\n/* harmony export */ });\n/* harmony import */ var _lastres_output_packet__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @lastres/output-packet */ \"./js-src/output-packet.ts\");\n\nclass MoveToPacket extends _lastres_output_packet__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n constructor(location) {\n super();\n this.location = location;\n }\n command() {\n return 'move_to';\n }\n data() {\n return {\n location: this.location.location.identifier,\n area: this.location.area.identifier,\n super_area: this.location.super_area.identifier,\n planet: this.location.planet.identifier\n };\n }\n}\n\n\n//# sourceURL=webpack://LasTres/./js-src/output-packet/move-to.ts?");
/***/ }),
/***/ "./js-src/output-packet/ping.ts":
/*!**************************************!*\
!*** ./js-src/output-packet/ping.ts ***!