xwords/xwords4/wasm/main.c

799 lines
23 KiB
C
Raw Normal View History

2021-02-06 17:54:08 +01:00
/* -*- compile-command: "cd ../wasm && make main.html -j3"; -*- */
/*
* Copyright 2021 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
2021-02-01 17:30:34 +01:00
2021-02-02 22:49:27 +01:00
#include <sys/time.h>
2021-02-01 17:30:34 +01:00
#include <stdio.h>
2021-02-02 05:13:25 +01:00
#include <stdarg.h>
2021-02-01 17:30:34 +01:00
#include <SDL2/SDL.h>
2021-02-02 18:08:41 +01:00
#include <SDL2/SDL_ttf.h>
2021-02-01 17:30:34 +01:00
#include <emscripten.h>
#include <unistd.h>
#include <stdlib.h>
2021-02-02 05:13:25 +01:00
#include "game.h"
2021-02-04 01:19:16 +01:00
#include "device.h"
2021-02-02 05:13:25 +01:00
#include "mempool.h"
2021-02-07 21:48:06 +01:00
#include "nli.h"
#include "strutils.h"
2021-02-10 23:16:22 +01:00
#include "movestak.h"
2021-02-02 05:13:25 +01:00
#include "main.h"
2021-02-02 05:13:25 +01:00
#include "wasmdraw.h"
#include "wasmutil.h"
#include "wasmdutil.h"
#include "wasmdict.h"
2021-02-01 17:30:34 +01:00
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
2021-02-02 05:13:25 +01:00
#define WASM_BOARD_LEFT 0
#define WASM_HOR_SCORE_TOP 0
#define WINDOW_WIDTH 400
#define WINDOW_HEIGHT 600
2021-02-07 02:36:53 +01:00
#define BDWIDTH WINDOW_WIDTH
#define BDHEIGHT WINDOW_HEIGHT
2021-02-01 17:30:34 +01:00
2021-02-06 17:54:08 +01:00
#define KEY_GAME "the_game"
#define DICTNAME "assets_dir/CollegeEng_2to8.xwd"
2021-02-06 17:54:08 +01:00
#define BUTTON_OK "OK"
#define BUTTON_CANCEL "Cancel"
typedef void (*AlertProc)(void* closure, const char* button);
/* typedef struct _Buttons { */
/* int nButtons; */
/* const char** buttons; */
/* } Buttons; */
EM_JS(void, call_dialog, (const char* str, const char** but_strs,
AlertProc proc, void* closure), {
var buttons = [];
for ( let ii = 0; ii < 3; ++ii ) {
const mem = HEAP32[(but_strs + (ii * 4)) >> 2];
if ( 0 == mem ) {
break;
}
const str = UTF8ToString(mem);
buttons.push(str);
}
nbDialog(UTF8ToString(str), buttons, proc, closure);
} );
2021-02-10 02:53:30 +01:00
EM_JS(void, call_haveDevID, (void* closure, const char* devid), {
onHaveDevID(closure, UTF8ToString(devid));
2021-02-07 21:48:06 +01:00
});
2021-02-10 03:56:13 +01:00
EM_JS(bool, call_mqttSend, (const char* topic, const uint8_t* ptr, int len), {
2021-02-07 21:48:06 +01:00
let topStr = UTF8ToString(topic);
let buffer = new Uint8Array(Module.HEAPU8.buffer, ptr, len);
2021-02-10 03:56:13 +01:00
return mqttSend(topStr, buffer);
2021-02-07 21:48:06 +01:00
});
2021-02-02 23:52:25 +01:00
2021-02-10 22:21:28 +01:00
typedef void (*JSCallback)(void* closure);
EM_JS(void, jscallback_set, (JSCallback proc, void* closure, int inMS), {
let timerproc = function(closure) {
ccall('jscallback', null, ['number', 'number'], [proc, closure]);
};
setTimeout( timerproc, inMS, closure );
});
2021-02-11 00:46:52 +01:00
EM_JS(void, setButtonText, (const char* id, const char* text), {
let jsid = UTF8ToString(id);
let jstext = UTF8ToString(text);
document.getElementById(jsid).textContent = jstext;
});
2021-02-10 21:18:15 +01:00
static void updateScreen( Globals* globals, bool doSave );
2021-02-06 18:23:39 +01:00
static void
call_alert( const char* msg )
{
const char* buttons[] = { BUTTON_OK, NULL };
call_dialog( msg, buttons, NULL, NULL );
}
2021-02-07 21:48:06 +01:00
static XP_S16
send_msg( XWEnv xwe, const XP_U8* buf, XP_U16 len,
const XP_UCHAR* msgNo, const CommsAddrRec* addr,
CommsConnType conType, XP_U32 gameID, void* closure )
{
XP_S16 nSent = -1;
LOG_FUNC();
Globals* globals = (Globals*)closure;
if ( addr_hasType( addr, COMMS_CONN_MQTT ) ) {
MQTTDevID devID = addr->u.mqtt.devID;
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(globals->mpool)
globals->vtMgr );
dvc_makeMQTTMessage( globals->dutil, NULL, stream,
gameID, buf, len );
XP_UCHAR topic[64];
formatMQTTTopic( &devID, topic, sizeof(topic) );
XP_U16 streamLen = stream_getSize( stream );
XP_LOGFF( "calling call_mqttSend" );
2021-02-10 03:56:13 +01:00
if ( call_mqttSend( topic, stream_getPtr( stream ), streamLen ) ) {
nSent = len;
}
2021-02-07 21:48:06 +01:00
XP_LOGFF( "back from call_mqttSend" );
stream_destroy( stream, NULL );
}
LOG_RETURNF( "%d", nSent );
return nSent;
}
2021-02-11 00:46:52 +01:00
static void
updateTradeButton( Globals* globals )
{
XP_Bool inTrade = board_inTrade( globals->game.board, NULL );
const char* text = inTrade ? "Cancel trade" : "Trade";
setButtonText( "trade", text );
}
2021-02-02 05:13:25 +01:00
static void
initDeviceGlobals( Globals* globals )
2021-02-02 05:13:25 +01:00
{
globals->cp.showBoardArrow = XP_TRUE;
2021-02-02 22:49:27 +01:00
globals->cp.allowPeek = XP_TRUE;
2021-02-03 20:47:03 +01:00
// globals->cp.showRobotScores = XP_TRUE;
2021-02-03 04:20:26 +01:00
globals->cp.sortNewTiles = XP_TRUE;
globals->cp.showColors = XP_TRUE;
2021-02-02 05:13:25 +01:00
2021-02-07 21:48:06 +01:00
globals->procs.send = send_msg;
globals->procs.closure = globals;
#ifdef MEMDEBUG
globals->mpool = mpool_make( "wasm" );
#endif
globals->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(globals->mpool) );
globals->dutil = wasm_dutil_make( MPPARM(globals->mpool) globals->vtMgr, globals );
globals->dictMgr = dmgr_make( MPPARM_NOCOMMA(globals->mpool) );
globals->dict = wasm_dictionary_make( MPPARM(globals->mpool) NULL,
globals, DICTNAME, true );
2021-02-07 21:48:06 +01:00
dict_ref( globals->dict, NULL );
globals->draw = wasm_draw_make( MPPARM(globals->mpool)
WINDOW_WIDTH, WINDOW_HEIGHT );
2021-02-04 01:19:16 +01:00
MQTTDevID devID;
dvc_getMQTTDevID( globals->dutil, NULL, &devID );
2021-02-07 21:48:06 +01:00
XP_UCHAR buf[32];
XP_SNPRINTF( buf, VSIZE(buf), MQTTDevID_FMT, devID );
XP_LOGFF( "got mqtt devID: %s", buf );
2021-02-10 02:53:30 +01:00
call_haveDevID( globals, buf );
2021-02-07 21:48:06 +01:00
}
static void
startGame( Globals* globals )
{
LOG_FUNC();
BoardDims dims;
board_figureLayout( globals->game.board, NULL, &globals->gi,
WASM_BOARD_LEFT, WASM_HOR_SCORE_TOP, BDWIDTH, BDHEIGHT,
110, 150, 200, BDWIDTH-25, BDWIDTH/15, BDHEIGHT/15,
XP_FALSE, &dims );
XP_LOGFF( "calling board_applyLayout" );
board_applyLayout( globals->game.board, NULL, &dims );
XP_LOGFF( "calling model_setDictionary" );
model_setDictionary( globals->game.model, NULL, globals->dict );
if ( SERVER_ISCLIENT == globals->gi.serverRole ) {
server_initClientConnection( globals->game.server, NULL );
}
(void)server_do( globals->game.server, NULL ); /* assign tiles, etc. */
2021-02-10 02:53:30 +01:00
if ( !!globals->game.comms ) {
comms_resendAll( globals->game.comms, NULL, COMMS_CONN_MQTT, XP_TRUE );
}
2021-02-07 21:48:06 +01:00
2021-02-10 21:18:15 +01:00
updateScreen( globals, true );
2021-02-07 21:48:06 +01:00
LOG_RETURN_VOID();
}
static bool
gameFromInvite( Globals* globals, const NetLaunchInfo* invite )
{
2021-02-10 21:18:15 +01:00
bool loaded = false;
bool needsLoad = true;
XP_LOGFF( "model: %p", globals->game.model );
if ( NULL != globals->game.model ) {
XP_LOGFF( "have game: TRUE" );
/* there's a current game. Ignore the invitation if it has the same
gameID. Otherwise ask to replace */
if ( globals->gi.gameID == invite->gameID ) {
XP_LOGFF( "duplicate invite; ignoring" );
needsLoad = false;
} else {
call_alert( "Invitation received; replace current game?" );
// needsLoad = false;
2021-02-10 21:18:15 +01:00
}
} else if ( invite->lang != 1 ) {
call_alert( "Invitations are only supported for play in English right now." );
2021-02-10 21:18:15 +01:00
needsLoad = false;
}
if ( needsLoad ) {
if ( !!globals->util ) {
game_dispose( &globals->game, NULL );
wasm_util_destroy( globals->util );
globals->util = NULL;
}
gi_disposePlayerInfo( MPPARM(globals->mpool) &globals->gi );
XP_MEMSET( &globals->gi, 0, sizeof(globals->gi) );
globals->util = wasm_util_make( MPPARM(globals->mpool) &globals->gi,
globals->dutil, globals );
2021-02-10 21:18:15 +01:00
loaded = game_makeFromInvite( MPPARM(globals->mpool) NULL, invite,
&globals->game, &globals->gi,
globals->dict, NULL,
globals->util, globals->draw,
&globals->cp, &globals->procs );
2021-02-10 21:18:15 +01:00
} else {
loaded = true;
}
2021-02-10 21:18:15 +01:00
LOG_RETURNF( "%d", loaded );
return loaded;
}
static bool
loadSavedGame( Globals* globals )
{
bool loaded = false;
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(globals->mpool)
globals->vtMgr );
dutil_loadStream( globals->dutil, NULL, KEY_GAME, NULL, stream );
if ( 0 < stream_getSize( stream ) ) {
XP_ASSERT( !globals->util );
globals->util = wasm_util_make( MPPARM(globals->mpool) &globals->gi,
2021-02-10 21:18:15 +01:00
globals->dutil, globals );
XP_LOGFF( "there's a saved game!!" );
loaded = game_makeFromStream( MPPARM(globals->mpool) NULL, stream,
&globals->game, &globals->gi,
globals->dict, NULL,
globals->util, globals->draw,
&globals->cp, &globals->procs );
if ( loaded ) {
updateScreen( globals, false );
}
2021-02-11 00:46:52 +01:00
updateTradeButton( globals );
2021-02-10 21:18:15 +01:00
}
stream_destroy( stream, NULL );
return loaded;
}
static void
2021-02-10 21:18:15 +01:00
loadAndDraw( Globals* globals, const NetLaunchInfo* invite,
bool forceNew, bool p0robot, bool p1robot )
{
if ( !!globals->util ) {
game_dispose( &globals->game, NULL );
wasm_util_destroy( globals->util );
globals->util = NULL;
}
2021-02-10 21:18:15 +01:00
bool haveGame;
if ( forceNew ) {
haveGame = false;
} else {
/* First, load any saved game. We need it e.g. to confirm that an incoming
invite is a dup and should be dropped. */
haveGame = loadSavedGame( globals );
2021-02-02 05:13:25 +01:00
2021-02-10 21:18:15 +01:00
if ( !!invite ) {
haveGame = gameFromInvite( globals, invite );
}
}
2021-02-02 05:13:25 +01:00
2021-02-10 21:18:15 +01:00
if ( !haveGame ) {
globals->gi.serverRole = SERVER_STANDALONE;
globals->gi.phoniesAction = PHONIES_WARN;
globals->gi.gameID = 0;
globals->gi.nPlayers = 2;
globals->gi.boardSize = 15;
globals->gi.players[0].name = copyString( globals->mpool, "Player 1" );
globals->gi.players[0].isLocal = XP_TRUE;
globals->gi.players[0].robotIQ = p0robot ? 99 : 0;
2021-02-10 21:18:15 +01:00
globals->gi.players[1].name = copyString( globals->mpool, "Player 2" );
globals->gi.players[1].isLocal = XP_TRUE;
globals->gi.players[1].robotIQ = p1robot ? 99 : 0;
2021-02-06 18:23:39 +01:00
globals->util = wasm_util_make( MPPARM(globals->mpool) &globals->gi,
2021-02-10 21:18:15 +01:00
globals->dutil, globals );
2021-02-06 18:23:39 +01:00
XP_LOGFF( "calling game_makeNewGame()" );
game_makeNewGame( MPPARM(globals->mpool) NULL,
&globals->game, &globals->gi,
globals->util, globals->draw,
&globals->cp, &globals->procs );
}
2021-02-02 05:13:25 +01:00
2021-02-07 21:48:06 +01:00
startGame( globals );
}
2021-02-02 05:13:25 +01:00
2021-02-07 21:48:06 +01:00
void
main_gameFromInvite( Globals* globals, const NetLaunchInfo* invite )
{
if ( gameFromInvite( globals, invite ) ) {
2021-02-07 21:48:06 +01:00
startGame( globals );
}
}
void
main_onGameMessage( Globals* globals, XP_U32 gameID,
const CommsAddrRec* from, XWStreamCtxt* stream )
{
XP_Bool draw = game_receiveMessage( &globals->game, NULL, stream, from );
if ( draw ) {
2021-02-10 21:18:15 +01:00
updateScreen( globals, true );
}
}
2021-02-07 21:48:06 +01:00
void
main_sendOnClose( XWStreamCtxt* stream, XWEnv env, void* closure )
{
Globals* globals = (Globals*)closure;
XP_LOGFF( "called with msg of len %d", stream_getSize(stream) );
(void)comms_send( globals->game.comms, NULL, stream );
2021-02-02 05:13:25 +01:00
}
2021-02-01 17:30:34 +01:00
2021-02-10 23:16:22 +01:00
void
main_playerScoreHeld( Globals* globals, XP_U16 player )
{
LastMoveInfo lmi;
XP_UCHAR buf[128];
if ( model_getPlayersLastScore( globals->game.model, NULL, player, &lmi ) ) {
switch ( lmi.moveType ) {
case ASSIGN_TYPE:
XP_SNPRINTF( buf, sizeof(buf), "Tiles assigned to %s", lmi.names[0] );
break;
case MOVE_TYPE:
XP_SNPRINTF( buf, sizeof(buf), "%s formed %s for %d points", lmi.names[0],
lmi.word, lmi.score );
break;
case TRADE_TYPE:
XP_SNPRINTF( buf, sizeof(buf), "%s traded %d tiles", lmi.names[0],
lmi.nTiles );
break;
default:
buf[0] = '\0';
}
}
if ( buf[0] ) {
call_alert( buf );
}
}
2021-02-13 21:16:09 +01:00
void
main_showGameOver( Globals* globals )
{
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(globals->mpool)
globals->vtMgr );
server_writeFinalScores( globals->game.server, NULL, stream );
stream_putU8( stream, 0 );
call_alert( (const XP_UCHAR*)stream_getPtr( stream ) );
stream_destroy( stream, NULL );
}
2021-02-02 22:49:27 +01:00
static time_t
getCurMS()
{
struct timeval tv;
gettimeofday( &tv, NULL );
time_t result = tv.tv_sec * 1000; /* convert to millis */
result += tv.tv_usec / 1000; /* convert to millis too */
// LOG_RETURNF( "%x", result );
return result;
}
2021-02-10 23:16:22 +01:00
void
main_clear_timer( Globals* globals, XWTimerReason why )
2021-02-02 22:49:27 +01:00
{
2021-02-10 23:16:22 +01:00
XP_LOGFF( "why: %d" );
2021-02-10 23:25:25 +01:00
// XP_ASSERT(0); fires when start new game
2021-02-03 04:20:26 +01:00
}
2021-02-10 23:16:22 +01:00
typedef struct _TimerClosure {
Globals* globals;
XWTimerReason why;
XWTimerProc proc;
void* closure;
} TimerClosure;
2021-02-02 22:49:27 +01:00
2021-02-10 23:16:22 +01:00
static void
onTimerFired( void* closure )
2021-02-07 21:48:06 +01:00
{
2021-02-10 23:16:22 +01:00
LOG_FUNC();
TimerClosure* tc = (TimerClosure*)closure;
XP_Bool draw = (*tc->proc)( tc->closure, NULL, tc->why );
if ( draw ) {
updateScreen( tc->globals, true );
}
XP_FREE( tc->globals->mpool, tc );
2021-02-07 21:48:06 +01:00
}
2021-02-02 22:49:27 +01:00
void
main_set_timer( Globals* globals, XWTimerReason why, XP_U16 when,
XWTimerProc proc, void* closure )
{
2021-02-10 23:16:22 +01:00
XP_LOGFF( "why: %d", why );
TimerClosure* tc = XP_MALLOC( globals->mpool, sizeof(*tc) );
tc->globals = globals;
tc->proc = proc;
tc->closure = closure;
tc->why = why;
if ( 0 == when ) {
when = 1;
}
when *= 1000; /* convert to ms */
2021-02-02 22:49:27 +01:00
2021-02-10 23:16:22 +01:00
jscallback_set( onTimerFired, tc, when );
2021-02-02 22:49:27 +01:00
}
typedef struct _QueryState {
Globals* globals;
QueryProc proc;
void* closure;
} QueryState;
static void
onQueryCalled( void* closure, const char* button )
{
QueryState* qs = (QueryState*)closure;
bool ok = 0 == strcmp( button, BUTTON_OK );
(*qs->proc)( qs->closure, ok );
updateTradeButton( qs->globals );
XP_FREE( qs->globals->mpool, qs );
}
2021-02-02 23:52:25 +01:00
void
main_query( Globals* globals, const XP_UCHAR* query, QueryProc proc, void* closure )
{
QueryState* qs = XP_MALLOC( globals->mpool, sizeof(*qs) );
qs->proc = proc;
qs->closure = closure;
qs->globals = globals;
const char* buttons[] = { BUTTON_CANCEL, BUTTON_OK, NULL };
call_dialog( query, buttons, onQueryCalled, qs );
2021-02-02 23:52:25 +01:00
}
2021-02-03 04:20:26 +01:00
void
main_alert( Globals* globals, const XP_UCHAR* msg )
{
call_alert( msg );
}
2021-02-10 22:21:28 +01:00
typedef struct _IdleClosure {
Globals* globals;
IdleProc proc;
void* closure;
} IdleClosure;
static void
onIdleFired( void* closure )
{
LOG_FUNC();
IdleClosure* ic = (IdleClosure*)closure;
XP_Bool draw = (*ic->proc)(ic->closure);
if ( draw ) {
updateScreen( ic->globals, true );
}
XP_FREE( ic->globals->mpool, ic );
}
2021-02-03 04:20:26 +01:00
void
main_set_idle( Globals* globals, IdleProc proc, void* closure )
{
2021-02-10 22:21:28 +01:00
LOG_FUNC();
IdleClosure* ic = XP_MALLOC( globals->mpool, sizeof(*ic) );
ic->globals = globals;
ic->proc = proc;
ic->closure = closure;
jscallback_set( onIdleFired, ic, 0 );
2021-02-03 04:20:26 +01:00
}
static XP_Bool
checkForEvent( Globals* globals )
{
XP_Bool handled;
XP_Bool draw = XP_FALSE;
BoardCtxt* board = globals->game.board;
SDL_Event event;
if ( SDL_PollEvent(&event) ) {
switch ( event.type ) {
case SDL_MOUSEBUTTONDOWN:
draw = event.button.button == SDL_BUTTON_LEFT
&& board_handlePenDown( board, NULL,
event.button.x, event.button.y,
&handled );
break;
case SDL_MOUSEBUTTONUP:
draw = event.button.button == SDL_BUTTON_LEFT
&& board_handlePenUp( board, NULL,
event.button.x, event.button.y );
break;
case SDL_MOUSEMOTION:
draw = board_handlePenMove( board, NULL,
event.motion.x, event.motion.y );
break;
default:
break;
}
}
// XP_LOGFF( "draw: %d", draw );
2021-02-03 04:20:26 +01:00
return draw;
}
static void
2021-02-10 21:18:15 +01:00
updateScreen( Globals* globals, bool doSave )
{
SDL_RenderClear( globals->renderer );
board_draw( globals->game.board, NULL );
wasm_draw_render( globals->draw, globals->renderer );
SDL_RenderPresent( globals->renderer );
2021-02-06 17:54:08 +01:00
/* Let's save state here too, though likely too often */
2021-02-10 21:18:15 +01:00
if ( doSave ) {
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(globals->mpool)
globals->vtMgr );
game_saveToStream( &globals->game, NULL, &globals->gi,
stream, ++globals->saveToken );
dutil_storeStream( globals->dutil, NULL, KEY_GAME, stream );
stream_destroy( stream, NULL );
game_saveSucceeded( &globals->game, NULL, globals->saveToken );
}
}
static void
doExit( Globals* globals )
{
call_alert( "Control passed to another tab" );
XP_MEMSET( globals, 0, sizeof(*globals) ); /* stop everything :-) */
// emscripten_cancel_main_loop(); <-- does nothing
}
2021-02-03 04:20:26 +01:00
static void
looper( void* closure )
{
Globals* globals = (Globals*)closure;
2021-02-10 23:16:22 +01:00
if ( checkForEvent( globals ) ) {
2021-02-10 21:18:15 +01:00
updateScreen( globals, true );
}
}
void
2021-02-10 23:19:16 +01:00
button( void* closure, const char* msg )
{
XP_Bool draw = XP_FALSE;
2021-02-10 23:19:16 +01:00
Globals* globals = (Globals*)closure;
BoardCtxt* board = globals->game.board;
XP_Bool redo;
if ( 0 == strcmp(msg, "hintdown") ) {
draw = board_requestHint( board, NULL, XP_TRUE, &redo );
} else if ( 0 == strcmp(msg, "hintup") ) {
draw = board_requestHint( board, NULL, XP_FALSE, &redo );
2021-02-10 21:18:15 +01:00
} else if ( 0 == strcmp(msg, "trade") ) {
2021-02-11 00:46:52 +01:00
draw = board_inTrade( board, NULL )
? board_endTrade( board )
: board_beginTrade( board, NULL );
updateTradeButton( globals );
2021-02-10 21:18:15 +01:00
} else if ( 0 == strcmp(msg, "commit") ) {
draw = board_commitTurn( board, NULL, XP_FALSE, XP_FALSE, NULL );
} else if ( 0 == strcmp(msg, "flip") ) {
draw = board_flip( board );
} else if ( 0 == strcmp(msg, "redo") ) {
draw = board_redoReplacedTiles( board, NULL )
|| board_replaceTiles( board, NULL );
} else if ( 0 == strcmp(msg, "vals") ) {
2021-02-12 02:57:18 +01:00
globals->cp.tvType = (globals->cp.tvType + 1) % TVT_N_ENTRIES;
draw = board_prefsChanged( board, &globals->cp );
} else if ( 0 == strcmp(msg, "exit") ) {
doExit( globals );
}
if ( draw ) {
2021-02-10 21:18:15 +01:00
updateScreen( globals, true );
}
}
static bool
loadInvite( Globals* globals, NetLaunchInfo* nlip,
int argc, const char** argv )
{
LOG_FUNC();
CurGameInfo gi = {0};
CommsAddrRec addr = {0};
MQTTDevID mqttDevID = 0;
XP_U16 nPlayersH = 0;
XP_U16 forceChannel = 0;
const XP_UCHAR* gameName = NULL;
const XP_UCHAR* inviteID = NULL;
for ( int ii = 0; ii < argc; ++ii ) {
const char* argp = argv[ii];
char* param = strchr(argp, '=');
if ( !param ) { /* no '='? */
continue;
}
char arg[8];
int argLen = param - argp;
XP_MEMCPY( arg, argp, argLen );
arg[argLen] = '\0';
++param; /* skip the '=' */
if ( 0 == strcmp( "lang", arg ) ) {
gi.dictLang = atoi(param);
} else if ( 0 == strcmp( "np", arg ) ) {
gi.nPlayers = atoi(param);
} else if ( 0 == strcmp( "nh", arg ) ) {
nPlayersH = atoi(param);
} else if ( 0 == strcmp( "gid", arg ) ) {
gi.gameID = atoi(param);
} else if ( 0 == strcmp( "fc", arg ) ) {
gi.forceChannel = atoi(param);
} else if ( 0 == strcmp( "nm", arg ) ) {
gameName = param;
} else if ( 0 == strcmp( "id", arg ) ) {
inviteID = param;
} else if ( 0 == strcmp( "wl", arg ) ) {
replaceStringIfDifferent( globals->mpool, &gi.dictName, param );
} else if ( 0 == strcmp( "r2id", arg ) ) {
if ( strToMQTTCDevID( param, &addr.u.mqtt.devID ) ) {
addr_addType( &addr, COMMS_CONN_MQTT );
} else {
XP_LOGFF( "bad devid %s", param );
}
} else {
XP_LOGFF( "dropping arg %s, param %s", arg, param );
}
}
bool success = 0 < nPlayersH &&
addr_hasType( &addr, COMMS_CONN_MQTT );
if ( success ) {
nli_init( nlip, &gi, &addr, nPlayersH, forceChannel );
if ( !!gameName ) {
nli_setGameName( nlip, gameName );
}
if ( !!inviteID ) {
nli_setInviteID( nlip, inviteID );
}
LOGNLI( nlip );
}
gi_disposePlayerInfo( MPPARM(globals->mpool) &gi );
LOG_RETURNF( "%d", success );
return success;
}
2021-02-06 18:23:39 +01:00
static void
initNoReturn( int argc, const char** argv )
2021-02-02 05:13:25 +01:00
{
2021-02-04 05:18:15 +01:00
time_t now = getCurMS();
srandom( now );
2021-02-06 17:54:08 +01:00
XP_LOGFF( "called(srandom( %x )", now );
2021-02-04 05:18:15 +01:00
Globals* globals = calloc(1, sizeof(*globals));
NetLaunchInfo nli = {0};
NetLaunchInfo* nlip = NULL;
if ( loadInvite( globals, &nli, argc, argv ) ) {
nlip = &nli;
}
SDL_Init( SDL_INIT_EVENTS );
2021-02-02 18:08:41 +01:00
TTF_Init();
SDL_CreateWindowAndRenderer( WINDOW_WIDTH, WINDOW_HEIGHT, 0,
&globals->window, &globals->renderer );
2021-02-01 17:30:34 +01:00
/* whip the canvas to background */
SDL_SetRenderDrawColor( globals->renderer, 155, 155, 155, 255 );
SDL_RenderClear( globals->renderer );
initDeviceGlobals( globals );
2021-02-02 05:13:25 +01:00
2021-02-10 21:18:15 +01:00
loadAndDraw( globals, nlip, false, false, true );
2021-02-06 18:23:39 +01:00
emscripten_set_main_loop_arg( looper, globals, -1, 1 );
}
typedef struct _NewgameState {
Globals* globals;
bool p0;
bool p1;
} NewgameState;
static void
onNewgameResponse( void* closure, const char* button )
{
NewgameState* ngs = (NewgameState*)closure;
Globals* globals = ngs->globals;
if ( 0 == strcmp( button, BUTTON_OK ) ) {
loadAndDraw( ngs->globals, NULL, true, ngs->p0, ngs->p1 );
}
XP_FREE( globals->mpool, ngs );
}
2021-02-04 00:37:50 +01:00
void
2021-02-10 23:25:25 +01:00
newgame( void* closure, bool p0, bool p1 )
2021-02-04 00:37:50 +01:00
{
2021-02-10 23:25:25 +01:00
Globals* globals = (Globals*)closure;
NewgameState* ngs = XP_MALLOC( globals->mpool, sizeof(*ngs) );
ngs->globals = globals;
ngs->p0 = p0;
ngs->p1 = p1;
2021-02-04 00:37:50 +01:00
XP_LOGFF( "(args: %d,%d)", p0, p1 );
const char* query = "Are you sure you want to replace the current game?";
const char* buttons[] = { BUTTON_CANCEL, BUTTON_OK, NULL };
call_dialog( query, buttons, onNewgameResponse, ngs );
2021-02-04 00:37:50 +01:00
}
2021-02-07 21:48:06 +01:00
void
2021-02-10 02:53:30 +01:00
gotMQTTMsg( void* closure, int len, const uint8_t* msg )
2021-02-07 21:48:06 +01:00
{
2021-02-10 02:53:30 +01:00
Globals* globals = (Globals*)closure;
dvc_parseMQTTPacket( globals->dutil, NULL, msg, len );
2021-02-07 21:48:06 +01:00
}
2021-02-10 22:21:28 +01:00
void
jscallback( JSCallback proc, void* closure )
{
LOG_FUNC();
(*proc)(closure);
}
void
onDlgButton( AlertProc proc, void* closure, const char* button )
{
if ( !!proc ) {
(*proc)( closure, button );
}
}
int
main( int argc, const char** argv )
{
2021-02-06 18:23:39 +01:00
XP_LOGFF( "(argc=%d)", argc );
initNoReturn( argc, argv );
2021-02-01 17:30:34 +01:00
return 0;
}