mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-27 09:58:45 +01:00
Merge branch 'android_branch' into gtk_multigame
Conflicts: xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java xwords4/common/comms.c xwords4/linux/cursesmain.c xwords4/linux/cursesmain.h xwords4/linux/gtkmain.c xwords4/linux/gtkmain.h xwords4/linux/linuxmain.c xwords4/linux/main.h xwords4/linux/scripts/discon_ok2.sh xwords4/relay/xwrelay.cpp (Note: The curses app crashes on exit with mempool assertions, but that's a problem before the merge.)
This commit is contained in:
commit
d50c808f96
68 changed files with 1428 additions and 603 deletions
|
@ -22,11 +22,21 @@
|
||||||
to come from a domain that you own or have control over. -->
|
to come from a domain that you own or have control over. -->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.eehouse.android.xw4"
|
package="org.eehouse.android.xw4"
|
||||||
android:versionCode="52"
|
android:versionCode="55"
|
||||||
android:versionName="@string/app_version"
|
android:versionName="@string/app_version"
|
||||||
>
|
>
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="8" />
|
<!-- BE SURE TO MODIFY project.project AND the variable TARGET in
|
||||||
|
../scripts/setup_local_props.sh if targetSdkVersion changes!!!
|
||||||
|
-->
|
||||||
|
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="11" />
|
||||||
|
|
||||||
|
<supports-screens android:resizeable="true"
|
||||||
|
android:smallScreens="true"
|
||||||
|
android:normalScreens="true"
|
||||||
|
android:largeScreens="true"
|
||||||
|
android:xlargeScreens="true"
|
||||||
|
/>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
@ -38,6 +48,10 @@
|
||||||
<uses-permission android:name="android.permission.READ_SMS" />
|
<uses-permission android:name="android.permission.READ_SMS" />
|
||||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||||
|
|
||||||
|
<uses-feature android:name="android.hardware.telephony"
|
||||||
|
android:required = "false"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- GCM stuff -->
|
<!-- GCM stuff -->
|
||||||
<permission android:name="org.eehouse.android.xw4.permission.C2D_MESSAGE"
|
<permission android:name="org.eehouse.android.xw4.permission.C2D_MESSAGE"
|
||||||
android:protectionLevel="signature" />
|
android:protectionLevel="signature" />
|
||||||
|
|
|
@ -486,14 +486,14 @@ and_draw_drawTrayDivider( DrawCtx* dctx, const XP_Rect* rect, CellFlags flags )
|
||||||
static void
|
static void
|
||||||
and_draw_score_pendingScore( DrawCtx* dctx, const XP_Rect* rect,
|
and_draw_score_pendingScore( DrawCtx* dctx, const XP_Rect* rect,
|
||||||
XP_S16 score, XP_U16 playerNum,
|
XP_S16 score, XP_U16 playerNum,
|
||||||
CellFlags flags )
|
XP_S16 curTurn, CellFlags flags )
|
||||||
{
|
{
|
||||||
DRAW_CBK_HEADER( "score_pendingScore", "(Landroid/graphics/Rect;III)V" );
|
DRAW_CBK_HEADER( "score_pendingScore", "(Landroid/graphics/Rect;IIII)V" );
|
||||||
|
|
||||||
jobject jrect = makeJRect( draw, JCACHE_RECT0, rect );
|
jobject jrect = makeJRect( draw, JCACHE_RECT0, rect );
|
||||||
|
|
||||||
(*env)->CallVoidMethod( env, draw->jdraw, mid,
|
(*env)->CallVoidMethod( env, draw->jdraw, mid,
|
||||||
jrect, score, playerNum, flags );
|
jrect, score, playerNum, curTurn, flags );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -3,11 +3,12 @@
|
||||||
#
|
#
|
||||||
# This file must be checked in Version Control Systems.
|
# This file must be checked in Version Control Systems.
|
||||||
#
|
#
|
||||||
# To customize properties used by the Ant build system use,
|
# To customize properties used by the Ant build system edit
|
||||||
# "ant.properties", and override values to adapt the script to your
|
# "ant.properties", and override values to adapt the script to your
|
||||||
# project structure.
|
# project structure.
|
||||||
|
#
|
||||||
|
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||||
|
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||||
|
|
||||||
# Indicates whether an apk should be generated for each density.
|
|
||||||
split.density=false
|
|
||||||
# Project target.
|
# Project target.
|
||||||
target=android-8
|
target=Google Inc.:Google APIs:11
|
||||||
|
|
|
@ -57,22 +57,10 @@
|
||||||
style="@style/toolbar_button"
|
style="@style/toolbar_button"
|
||||||
android:src="@drawable/shuffle"
|
android:src="@drawable/shuffle"
|
||||||
/>
|
/>
|
||||||
<ImageButton android:id="@+id/zoom_button_horizontal"
|
|
||||||
style="@style/toolbar_button"
|
|
||||||
android:src="@drawable/zoom"
|
|
||||||
/>
|
|
||||||
<ImageButton android:id="@+id/undo_button_horizontal"
|
<ImageButton android:id="@+id/undo_button_horizontal"
|
||||||
style="@style/toolbar_button"
|
style="@style/toolbar_button"
|
||||||
android:src="@drawable/undo"
|
android:src="@drawable/undo"
|
||||||
/>
|
/>
|
||||||
<ImageButton android:id="@+id/values_button_horizontal"
|
|
||||||
style="@style/toolbar_button"
|
|
||||||
android:src="@drawable/values"
|
|
||||||
/>
|
|
||||||
<ImageButton android:id="@+id/flip_button_horizontal"
|
|
||||||
style="@style/toolbar_button"
|
|
||||||
android:src="@drawable/flip"
|
|
||||||
/>
|
|
||||||
<ImageButton android:id="@+id/dictlist_button_horizontal"
|
<ImageButton android:id="@+id/dictlist_button_horizontal"
|
||||||
style="@style/toolbar_button"
|
style="@style/toolbar_button"
|
||||||
android:src="@drawable/dicticon"
|
android:src="@drawable/dicticon"
|
||||||
|
@ -81,6 +69,18 @@
|
||||||
style="@style/toolbar_button"
|
style="@style/toolbar_button"
|
||||||
android:src="@drawable/stat_notify_chat"
|
android:src="@drawable/stat_notify_chat"
|
||||||
/>
|
/>
|
||||||
|
<ImageButton android:id="@+id/values_button_horizontal"
|
||||||
|
style="@style/toolbar_button"
|
||||||
|
android:src="@drawable/values"
|
||||||
|
/>
|
||||||
|
<ImageButton android:id="@+id/flip_button_horizontal"
|
||||||
|
style="@style/toolbar_button"
|
||||||
|
android:src="@drawable/flip"
|
||||||
|
/>
|
||||||
|
<ImageButton android:id="@+id/zoom_button_horizontal"
|
||||||
|
style="@style/toolbar_button"
|
||||||
|
android:src="@drawable/zoom"
|
||||||
|
/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingRight="8dp">
|
android:paddingRight="8dp"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
>
|
||||||
|
|
||||||
<TextView android:id="@+id/desc"
|
<TextView android:id="@+id/desc"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -5,26 +5,28 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<b>Crosswords 4.4 beta 60 release</b>
|
<b>Crosswords 4.4 beta 63 release</b>
|
||||||
|
|
||||||
<h3>New with this release</h3>
|
<h3>New with this release</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Allow alternate spellings for tiles in the Find field in the
|
<li>Reduce amount of network traffic required to get moves</li>
|
||||||
wordlist browser, e.g. 'a' for 'A' and 'L-L' for 'L·L' (in
|
|
||||||
Catalan). The new wordlist format requires this upgrade, so I will
|
|
||||||
wait a few weeks before releasing new wordlists. </li>
|
|
||||||
|
|
||||||
<li>Upgrade built-in English wordlists.</li>
|
<li>Make all wordlists available from wordlists button below board
|
||||||
|
(on Android 3.0 and above)</li>
|
||||||
|
|
||||||
<li>Don't run SMSService if play via SSM is disabled</li>
|
<li>Dim pending score counter (at right end of tray) when it's not
|
||||||
|
that player's turn or game's over</li>
|
||||||
|
|
||||||
|
<li>Move more frequently used game buttons to left where they're
|
||||||
|
more visible</li>
|
||||||
|
|
||||||
|
<li>Fix a few bugs</li>
|
||||||
|
|
||||||
<li>Fix bug with invites to SMS games where invitee is missing
|
|
||||||
wordlist</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>Next up</h3>
|
<h3>Next up</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Improve communication with relay</li>
|
<li>Improve communication with relay, part 2</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>(The full changelog
|
<p>(The full changelog
|
||||||
|
|
|
@ -15,19 +15,19 @@
|
||||||
-->
|
-->
|
||||||
<!-- These two messages appear at the top of the list of games
|
<!-- These two messages appear at the top of the list of games
|
||||||
(unless the hide_intro preferences checkbox is checked.)-->
|
(unless the hide_intro preferences checkbox is checked.)-->
|
||||||
<string name="empty_games_list">Use o botão abaixo para criar um
|
<string name="empty_games_list">Use o botão abaixo para criar um novo
|
||||||
jogo. Toque num jogo existente para continuá-lo ou dê um toque
|
jogo. Selecione um jogo existente para continuá-lo ou dê um toque
|
||||||
longo para outras opções.</string>
|
longo para outras opções.</string>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<string name="empty_games_list2">Você pode esconder essa mensagem
|
<string name="empty_games_list2">Você pode esconder essa mensagem
|
||||||
e os botões abaixo na seção Aparência das Configurações (acessadas
|
e os botões abaixo na seção Aparência das Configurações (acessada
|
||||||
através do botão menu do seu dispositivo.)</string>
|
através do botão menu do seu dispositivo.)</string>
|
||||||
|
|
||||||
<!-- Text of button at bottom of main games-list screen and of
|
<!-- Text of button at bottom of main games-list screen and of
|
||||||
menuitem in main games-list screen's menu. (The botton can
|
menuitem in main games-list screen's menu. (The botton can
|
||||||
be hidden in the same way as the above text.) -->
|
be hidden in the same way as the above text.) -->
|
||||||
<string name="button_new_game">Adicionar jogo</string>
|
<string name="button_new_game">Novo jogo</string>
|
||||||
<string name="button_new_group">Adicionar grupo</string>
|
<string name="button_new_group">Novo grupo</string>
|
||||||
|
|
||||||
<!-- When the game list is empty and the above messages and button
|
<!-- When the game list is empty and the above messages and button
|
||||||
are hidden via preferences, this text is shown -->
|
are hidden via preferences, this text is shown -->
|
||||||
|
@ -114,13 +114,13 @@
|
||||||
<string name="gamel_menu_checkmoves">Checar jogadas</string>
|
<string name="gamel_menu_checkmoves">Checar jogadas</string>
|
||||||
|
|
||||||
<!-- Text of progress indicator shown while check is being conducted -->
|
<!-- Text of progress indicator shown while check is being conducted -->
|
||||||
<string name="msgs_progress">Checando retransmissor por jogadas
|
<string name="msgs_progress">Procurando jogadas no servidor
|
||||||
etc...</string>
|
etc...</string>
|
||||||
|
|
||||||
<!-- If you choose the above option and have no networked games
|
<!-- If you choose the above option and have no networked games
|
||||||
you get this error message -->
|
you get this error message -->
|
||||||
<string name="no_games_to_refresh">Nenhum jogo encontrado que conecta
|
<string name="no_games_to_refresh">Nenhum jogo encontrado que conecta
|
||||||
pelo retransmissor.</string>
|
pelo servidor.</string>
|
||||||
|
|
||||||
<!-- Deletes all games on the device (after confirmation) -->
|
<!-- Deletes all games on the device (after confirmation) -->
|
||||||
<string name="gamel_menu_delete_all">Excluir todos</string>
|
<string name="gamel_menu_delete_all">Excluir todos</string>
|
||||||
|
@ -166,7 +166,7 @@
|
||||||
<!-- If you try to copy a networked game you get this error
|
<!-- If you try to copy a networked game you get this error
|
||||||
message. -->
|
message. -->
|
||||||
<string name="no_copy_network">Jogos que já se conectaram ao
|
<string name="no_copy_network">Jogos que já se conectaram ao
|
||||||
retransmissor não podem ser copiados. Use \"Novo a partir de\"
|
servidor não podem ser copiados. Use \"Novo a partir de\"
|
||||||
para uma cópia pronta para jogar com todas as mesmas
|
para uma cópia pronta para jogar com todas as mesmas
|
||||||
configurações.</string>
|
configurações.</string>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
|
@ -412,13 +412,13 @@
|
||||||
<!-- These are the three choices in the popup above whose text is
|
<!-- These are the three choices in the popup above whose text is
|
||||||
phonies_spinner_prompt -->
|
phonies_spinner_prompt -->
|
||||||
<!-- Don't care if words played are in the wordlist or not -->
|
<!-- Don't care if words played are in the wordlist or not -->
|
||||||
<string name="phonies_ignore">Ignorar falsas</string>
|
<string name="phonies_ignore">Ignorar palavras falsas</string>
|
||||||
<!-- warn player when word played is not in the wordlist, but
|
<!-- warn player when word played is not in the wordlist, but
|
||||||
allow him to play it. -->
|
allow him to play it. -->
|
||||||
<string name="phonies_warn">Avisar se for falsa</string>
|
<string name="phonies_warn">Avisar se a palavra for falsa</string>
|
||||||
<!-- Don't warn, but simply force to skip turn (give 0 points)
|
<!-- Don't warn, but simply force to skip turn (give 0 points)
|
||||||
when user attempts to play word not in the wordlist. -->
|
when user attempts to play word not in the wordlist. -->
|
||||||
<string name="phonies_disallow">Não permitir falsas</string>
|
<string name="phonies_disallow">Não permitir palavras falsas</string>
|
||||||
|
|
||||||
<!-- Shown when using the the Game configure screen to configure a
|
<!-- Shown when using the the Game configure screen to configure a
|
||||||
networked game and you try to make all players local. -->
|
networked game and you try to make all players local. -->
|
||||||
|
@ -536,16 +536,16 @@
|
||||||
|
|
||||||
<!-- Bonus value hint that's displayed in gray text in the colored
|
<!-- Bonus value hint that's displayed in gray text in the colored
|
||||||
bonus square. Double-letter -->
|
bonus square. Double-letter -->
|
||||||
<string name="bonus_l2x_summary">2L</string>
|
<string name="bonus_l2x_summary">L2</string>
|
||||||
<!-- Bonus value hint that's displayed in gray text in the colored
|
<!-- Bonus value hint that's displayed in gray text in the colored
|
||||||
bonus square. Double-word -->
|
bonus square. Double-word -->
|
||||||
<string name="bonus_w2x_summary">2P</string>
|
<string name="bonus_w2x_summary">P2</string>
|
||||||
<!-- Bonus value hint that's displayed in gray text in the colored
|
<!-- Bonus value hint that's displayed in gray text in the colored
|
||||||
bonus square. Triple-letter -->
|
bonus square. Triple-letter -->
|
||||||
<string name="bonus_l3x_summary">3L</string>
|
<string name="bonus_l3x_summary">L3</string>
|
||||||
<!-- Bonus value hint that's displayed in gray text in the colored
|
<!-- Bonus value hint that's displayed in gray text in the colored
|
||||||
bonus square. Triple-word -->
|
bonus square. Triple-word -->
|
||||||
<string name="bonus_w3x_summary">3P</string>
|
<string name="bonus_w3x_summary">P3</string>
|
||||||
|
|
||||||
<!-- displayed when you long-tap a scoreboard entry and there's no
|
<!-- displayed when you long-tap a scoreboard entry and there's no
|
||||||
most recent score to show -->
|
most recent score to show -->
|
||||||
|
@ -588,7 +588,7 @@
|
||||||
registered with the relay in this game. This should be seen
|
registered with the relay in this game. This should be seen
|
||||||
only once per game. -->
|
only once per game. -->
|
||||||
<string name="msg_relay_waiting">Dispositivo %1$d conectado ao
|
<string name="msg_relay_waiting">Dispositivo %1$d conectado ao
|
||||||
retransmissor na sala \"%2$s\". Esperando por %3$d jogador(es).</string>
|
servidor na sala \"%2$s\". Esperando por %3$d jogador(es).</string>
|
||||||
|
|
||||||
<!-- Text of "toast" shown when a game is notified by the relay
|
<!-- Text of "toast" shown when a game is notified by the relay
|
||||||
that all expected players have registered. At this point
|
that all expected players have registered. At this point
|
||||||
|
@ -612,7 +612,7 @@
|
||||||
usando esse nome. Mude o nome da sua ou tente novamente mais
|
usando esse nome. Mude o nome da sua ou tente novamente mais
|
||||||
tarde.</string>
|
tarde.</string>
|
||||||
<!-- (I believe this can no longer occur) -->
|
<!-- (I believe this can no longer occur) -->
|
||||||
<string name="msg_lost_other">O retransmissor perdeu contato com
|
<string name="msg_lost_other">O servidor perdeu contato com
|
||||||
outro dispositivo nesse jogos.</string>
|
outro dispositivo nesse jogos.</string>
|
||||||
|
|
||||||
<!-- When a game has been connected and the relay is notified that
|
<!-- When a game has been connected and the relay is notified that
|
||||||
|
@ -971,10 +971,10 @@
|
||||||
############################################################
|
############################################################
|
||||||
-->
|
-->
|
||||||
<!-- title of this sub-preference -->
|
<!-- title of this sub-preference -->
|
||||||
<string name="prefs_colors">Cores individuais</string>
|
<string name="prefs_colors">Cores</string>
|
||||||
<!-- clarification of the above -->
|
<!-- clarification of the above -->
|
||||||
<string name="prefs_colors_summary">Editar as cores usadas
|
<string name="prefs_colors_summary">Editar as cores usadas
|
||||||
no tabuleiro</string>
|
no jogo</string>
|
||||||
|
|
||||||
<!-- The remaining strings (down to the color edit dialog below)
|
<!-- The remaining strings (down to the color edit dialog below)
|
||||||
are showns as the names of editable colors and as the the
|
are showns as the names of editable colors and as the the
|
||||||
|
@ -1003,7 +1003,7 @@
|
||||||
touching in order to guide the fat-fingered (most of us) in
|
touching in order to guide the fat-fingered (most of us) in
|
||||||
operations that require accurately selecting a single square
|
operations that require accurately selecting a single square
|
||||||
on the board.-->
|
on the board.-->
|
||||||
<string name="clr_crosshairs">Cor das linhas</string>
|
<string name="clr_crosshairs">Cor das linhas de posição</string>
|
||||||
|
|
||||||
<!-- color of the tiles' background -->
|
<!-- color of the tiles' background -->
|
||||||
<string name="tile_back">Fundo das pedras</string>
|
<string name="tile_back">Fundo das pedras</string>
|
||||||
|
@ -1103,7 +1103,7 @@
|
||||||
is distracting, presumably because they're using tablets with
|
is distracting, presumably because they're using tablets with
|
||||||
large enough screens that they always know where they're
|
large enough screens that they always know where they're
|
||||||
tapping. -->
|
tapping. -->
|
||||||
<string name="hide_crosshairs">Desligar mira</string>
|
<string name="hide_crosshairs">Desligar linhas de posição</string>
|
||||||
<!-- explanation of the above -->
|
<!-- explanation of the above -->
|
||||||
<string name="hide_crosshairs_summary">Não indicar visualmente
|
<string name="hide_crosshairs_summary">Não indicar visualmente
|
||||||
a casa do tabuleiro selecionada</string>
|
a casa do tabuleiro selecionada</string>
|
||||||
|
@ -1644,7 +1644,7 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- shown when user chooses the gamel_menu_checkmoves menu -->
|
<!-- shown when user chooses the gamel_menu_checkmoves menu -->
|
||||||
<string name="not_again_sync">Essa ação procura no retransmissor
|
<string name="not_again_sync">Essa ação procura no servidor
|
||||||
por jogadas/mensagens pendentes para todos os jogos de rede e
|
por jogadas/mensagens pendentes para todos os jogos de rede e
|
||||||
marca aqueles com jogadas pendentes. Quando você abrir um jogo
|
marca aqueles com jogadas pendentes. Quando você abrir um jogo
|
||||||
marcado, ele fará a conexão e sincronização. (Numa versão futura
|
marcado, ele fará a conexão e sincronização. (Numa versão futura
|
||||||
|
@ -1729,7 +1729,7 @@
|
||||||
in the game but not the last either. So it will only occur
|
in the game but not the last either. So it will only occur
|
||||||
for games with more than two devices, which are rare. -->
|
for games with more than two devices, which are rare. -->
|
||||||
<string name="not_again_conndmid">Você conectou e entrou num
|
<string name="not_again_conndmid">Você conectou e entrou num
|
||||||
jogo no retransmissor. Você será notificado quando o(s)
|
jogo no servidor. Você será notificado quando o(s)
|
||||||
dispositivo(s) restantes(s) tiver(em) entrado na sua sala e
|
dispositivo(s) restantes(s) tiver(em) entrado na sua sala e
|
||||||
o jogo puder começar.</string>
|
o jogo puder começar.</string>
|
||||||
|
|
||||||
|
@ -1738,7 +1738,7 @@
|
||||||
game to do so, i.e. the game is now complete and you should
|
game to do so, i.e. the game is now complete and you should
|
||||||
expect play to begin. -->
|
expect play to begin. -->
|
||||||
<string name="not_again_conndall">Você conectou e entrou num jogo
|
<string name="not_again_conndall">Você conectou e entrou num jogo
|
||||||
no retransmissor, a sala agora está cheia. O dispositivo que criou
|
no servidor, a sala agora está cheia. O dispositivo que criou
|
||||||
a sala fará a distribuição inicial de pedras e o jogo pode
|
a sala fará a distribuição inicial de pedras e o jogo pode
|
||||||
começar.</string>
|
começar.</string>
|
||||||
|
|
||||||
|
@ -1853,7 +1853,7 @@
|
||||||
<string name="pick_url_titlef">Procurar %s em</string>
|
<string name="pick_url_titlef">Procurar %s em</string>
|
||||||
|
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<string name="board_menu_pass">Pular</string>
|
<string name="board_menu_pass">Pular vez</string>
|
||||||
|
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<string name="not_again_lookup">Esse botão permite que você
|
<string name="not_again_lookup">Esse botão permite que você
|
||||||
|
@ -1888,6 +1888,9 @@
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<string name="not_again_browse">Esse botão abre o navegador de
|
<string name="not_again_browse">Esse botão abre o navegador de
|
||||||
listas de palavras na lista do jogador atual.</string>
|
listas de palavras na lista do jogador atual.</string>
|
||||||
|
<string name="not_again_browseall">Esse botão abre o seletor de listas
|
||||||
|
de palavras na lista de sua escolha.</string>
|
||||||
|
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<string name="alert_empty_dictf">A lista de palavras %s só contém
|
<string name="alert_empty_dictf">A lista de palavras %s só contém
|
||||||
informações de pedras. Não há palavras para mostrar.</string>
|
informações de pedras. Não há palavras para mostrar.</string>
|
||||||
|
@ -2052,7 +2055,7 @@
|
||||||
<string name="game_list_tmp">Carregando resumo do jogo...</string>
|
<string name="game_list_tmp">Carregando resumo do jogo...</string>
|
||||||
|
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<string name="connstat_nonet">Este é um jogo independente. Não
|
<string name="connstat_nonet">Este é um jogo local. Não
|
||||||
há informações de rede.</string>
|
há informações de rede.</string>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<string name="connstat_net">Informações de rede para jogo conectado
|
<string name="connstat_net">Informações de rede para jogo conectado
|
||||||
|
@ -2073,7 +2076,7 @@
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<string name="connstat_noreceipt">Nenhuma mensagem foi recebida.</string>
|
<string name="connstat_noreceipt">Nenhuma mensagem foi recebida.</string>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<string name="connstat_relay">internet/retransmissor</string>
|
<string name="connstat_relay">internet/servidor</string>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<string name="connstat_sms">sms/texto</string>
|
<string name="connstat_sms">sms/texto</string>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
|
@ -2124,7 +2127,7 @@
|
||||||
<!-- Used in formatting final scores display -->
|
<!-- Used in formatting final scores display -->
|
||||||
<string name="str_resigned">Desistiu</string>
|
<string name="str_resigned">Desistiu</string>
|
||||||
<!-- Used in formatting final scores display -->
|
<!-- Used in formatting final scores display -->
|
||||||
<string name="str_winner">Ganhador</string>
|
<string name="str_winner">Vencedor</string>
|
||||||
|
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<string name="inform_dict_diffversionf">Você e o hospedeiro
|
<string name="inform_dict_diffversionf">Você e o hospedeiro
|
||||||
|
@ -2197,4 +2200,7 @@
|
||||||
<string name="square_tiles_summary">Mesmo se puderem ser mais altas</string>
|
<string name="square_tiles_summary">Mesmo se puderem ser mais altas</string>
|
||||||
|
|
||||||
<string name="change_groupf">Mover jogo %s</string>
|
<string name="change_groupf">Mover jogo %s</string>
|
||||||
|
|
||||||
|
<string name="show_wordlist_browser">Listas de palavras</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_version">4.4 beta 60</string>
|
<string name="app_version">4.4 beta 63</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -96,6 +96,7 @@
|
||||||
<string name="key_notagain_trading">key_notagain_trading</string>
|
<string name="key_notagain_trading">key_notagain_trading</string>
|
||||||
<string name="key_na_lookup">key_na_lookup</string>
|
<string name="key_na_lookup">key_na_lookup</string>
|
||||||
<string name="key_na_browse">key_na_browse</string>
|
<string name="key_na_browse">key_na_browse</string>
|
||||||
|
<string name="key_na_browseall">key_na_browseall</string>
|
||||||
<string name="key_na_values">key_na_values</string>
|
<string name="key_na_values">key_na_values</string>
|
||||||
<string name="key_enable_debug">key_enable_debug</string>
|
<string name="key_enable_debug">key_enable_debug</string>
|
||||||
<string name="key_download_path">key_download_path</string>
|
<string name="key_download_path">key_download_path</string>
|
||||||
|
|
|
@ -1797,9 +1797,7 @@
|
||||||
|
|
||||||
<!-- Another paragraph giving credit for work done other than by
|
<!-- Another paragraph giving credit for work done other than by
|
||||||
Eric House and translators -->
|
Eric House and translators -->
|
||||||
<string name="about_credits">Toolbar icons by Sarah Chu; other
|
<string name="about_credits">Toolbar icons by Sarah Chu.</string>
|
||||||
credits pending permission.</string>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- text of dialog showing the set of changes made since the last
|
<!-- text of dialog showing the set of changes made since the last
|
||||||
release -->
|
release -->
|
||||||
|
@ -1860,6 +1858,9 @@
|
||||||
<string name="not_again_browse">This button opens the wordlist
|
<string name="not_again_browse">This button opens the wordlist
|
||||||
browser on the current player\'s wordlist.</string>
|
browser on the current player\'s wordlist.</string>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
|
<string name="not_again_browseall">This button opens the wordlist
|
||||||
|
browser on the wordlist of your choice.</string>
|
||||||
|
<!-- -->
|
||||||
<string name="alert_empty_dictf">The wordlist %s contains only
|
<string name="alert_empty_dictf">The wordlist %s contains only
|
||||||
tile information. There are no words to browse.</string>
|
tile information. There are no words to browse.</string>
|
||||||
|
|
||||||
|
@ -2149,7 +2150,7 @@
|
||||||
<string name="game_name_group_title">Name group</string>
|
<string name="game_name_group_title">Name group</string>
|
||||||
|
|
||||||
<string name="cannot_delete_default_group">The group for new games
|
<string name="cannot_delete_default_group">The group for new games
|
||||||
cannot be deleted."</string>
|
cannot be deleted.</string>
|
||||||
|
|
||||||
<string name="no_move_onegroup">Moving is impossible until there
|
<string name="no_move_onegroup">Moving is impossible until there
|
||||||
is more than one group.</string>
|
is more than one group.</string>
|
||||||
|
@ -2164,4 +2165,6 @@
|
||||||
<string name="square_tiles_summary">Even if they can be taller</string>
|
<string name="square_tiles_summary">Even if they can be taller</string>
|
||||||
|
|
||||||
<string name="change_groupf">Move game %s</string>
|
<string name="change_groupf">Move game %s</string>
|
||||||
|
|
||||||
|
<string name="show_wordlist_browser">Wordlist browser</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
|
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 2009 - 2012 by Eric House (xwords@eehouse.org). All
|
* Copyright 2009 - 2013 by Eric House (xwords@eehouse.org). All
|
||||||
* rights reserved.
|
* rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -102,6 +102,7 @@ public class BoardActivity extends XWActivity
|
||||||
private static final int BT_PICK_ACTION = 17;
|
private static final int BT_PICK_ACTION = 17;
|
||||||
private static final int SMS_PICK_ACTION = 18;
|
private static final int SMS_PICK_ACTION = 18;
|
||||||
private static final int SMS_CONFIG_ACTION = 19;
|
private static final int SMS_CONFIG_ACTION = 19;
|
||||||
|
private static final int BUTTON_BROWSEALL_ACTION = 20;
|
||||||
|
|
||||||
private static final String DLG_TITLE = "DLG_TITLE";
|
private static final String DLG_TITLE = "DLG_TITLE";
|
||||||
private static final String DLG_TITLESTR = "DLG_TITLESTR";
|
private static final String DLG_TITLESTR = "DLG_TITLESTR";
|
||||||
|
@ -149,7 +150,7 @@ public class BoardActivity extends XWActivity
|
||||||
private boolean m_gameOver = false;
|
private boolean m_gameOver = false;
|
||||||
|
|
||||||
// call startActivityForResult synchronously
|
// call startActivityForResult synchronously
|
||||||
private Semaphore m_forResultWait = new Semaphore(0);
|
private Semaphore m_forResultWait = new Semaphore(0);
|
||||||
private int m_resultCode;
|
private int m_resultCode;
|
||||||
|
|
||||||
private Thread m_blockingThread;
|
private Thread m_blockingThread;
|
||||||
|
@ -903,9 +904,15 @@ public class BoardActivity extends XWActivity
|
||||||
Utils.showToast( BoardActivity.this, m_toastStr );
|
Utils.showToast( BoardActivity.this, m_toastStr );
|
||||||
m_toastStr = null;
|
m_toastStr = null;
|
||||||
break;
|
break;
|
||||||
|
case BUTTON_BROWSEALL_ACTION:
|
||||||
case BUTTON_BROWSE_ACTION:
|
case BUTTON_BROWSE_ACTION:
|
||||||
String dictName = m_gi.dictName( m_view.getCurPlayer() );
|
String curDict = m_gi.dictName( m_view.getCurPlayer() );
|
||||||
DictBrowseActivity.launch( this, dictName );
|
View button = m_toolbar.getViewFor( Toolbar.BUTTON_BROWSE_DICT );
|
||||||
|
if ( BUTTON_BROWSEALL_ACTION == id &&
|
||||||
|
DictsActivity.handleDictsPopup( this, button, curDict ) ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DictBrowseActivity.launch( this, curDict );
|
||||||
break;
|
break;
|
||||||
case PREV_HINT_ACTION:
|
case PREV_HINT_ACTION:
|
||||||
cmd = JNICmd.CMD_PREV_HINT;
|
cmd = JNICmd.CMD_PREV_HINT;
|
||||||
|
@ -1821,9 +1828,13 @@ public class BoardActivity extends XWActivity
|
||||||
private void populateToolbar()
|
private void populateToolbar()
|
||||||
{
|
{
|
||||||
m_toolbar.setListener( Toolbar.BUTTON_BROWSE_DICT,
|
m_toolbar.setListener( Toolbar.BUTTON_BROWSE_DICT,
|
||||||
R.string.not_again_browse,
|
R.string.not_again_browseall,
|
||||||
R.string.key_na_browse,
|
R.string.key_na_browseall,
|
||||||
BUTTON_BROWSE_ACTION );
|
BUTTON_BROWSEALL_ACTION );
|
||||||
|
m_toolbar.setLongClickListener( Toolbar.BUTTON_BROWSE_DICT,
|
||||||
|
R.string.not_again_browse,
|
||||||
|
R.string.key_na_browse,
|
||||||
|
BUTTON_BROWSE_ACTION );
|
||||||
m_toolbar.setListener( Toolbar.BUTTON_HINT_PREV,
|
m_toolbar.setListener( Toolbar.BUTTON_HINT_PREV,
|
||||||
R.string.not_again_hintprev,
|
R.string.not_again_hintprev,
|
||||||
R.string.key_notagain_hintprev,
|
R.string.key_notagain_hintprev,
|
||||||
|
|
|
@ -50,6 +50,7 @@ public class BoardView extends View implements DrawCtx, BoardHandler,
|
||||||
|
|
||||||
private static Bitmap s_bitmap; // the board
|
private static Bitmap s_bitmap; // the board
|
||||||
private static final int IN_TRADE_ALPHA = 0x3FFFFFFF;
|
private static final int IN_TRADE_ALPHA = 0x3FFFFFFF;
|
||||||
|
private static final int NOT_TURN_ALPHA = 0x3FFFFFFF;
|
||||||
private static final int PINCH_THRESHOLD = 40;
|
private static final int PINCH_THRESHOLD = 40;
|
||||||
private static final int SCORE_HT_DROP = 2;
|
private static final int SCORE_HT_DROP = 2;
|
||||||
private static final boolean DEBUG_DRAWFRAMES = false;
|
private static final boolean DEBUG_DRAWFRAMES = false;
|
||||||
|
@ -888,14 +889,19 @@ public class BoardView extends View implements DrawCtx, BoardHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
public void score_pendingScore( Rect rect, int score, int playerNum,
|
public void score_pendingScore( Rect rect, int score, int playerNum,
|
||||||
int flags )
|
int curTurn, int flags )
|
||||||
{
|
{
|
||||||
String text = score >= 0? String.format( "%d", score ) : "??";
|
String text = score >= 0? String.format( "%d", score ) : "??";
|
||||||
int otherIndx = (0 == (flags & CELL_ISCURSOR))
|
int otherIndx = (0 == (flags & CELL_ISCURSOR))
|
||||||
? CommonPrefs.COLOR_BACKGRND : CommonPrefs.COLOR_FOCUS;
|
? CommonPrefs.COLOR_BACKGRND : CommonPrefs.COLOR_FOCUS;
|
||||||
++rect.top;
|
++rect.top;
|
||||||
fillRectOther( rect, otherIndx );
|
fillRectOther( rect, otherIndx );
|
||||||
m_fillPaint.setColor( m_playerColors[playerNum] );
|
|
||||||
|
int playerColor = m_playerColors[playerNum];
|
||||||
|
if ( playerNum != curTurn ) {
|
||||||
|
playerColor &= NOT_TURN_ALPHA;
|
||||||
|
}
|
||||||
|
m_fillPaint.setColor( playerColor );
|
||||||
|
|
||||||
rect.bottom -= rect.height() / 2;
|
rect.bottom -= rect.height() / 2;
|
||||||
drawCentered( text, rect, null );
|
drawCentered( text, rect, null );
|
||||||
|
|
|
@ -136,21 +136,21 @@ public class DictImportActivity extends XWActivity {
|
||||||
}
|
}
|
||||||
} // class DownloadFilesTask
|
} // class DownloadFilesTask
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate( Bundle savedInstanceState )
|
protected void onCreate( Bundle savedInstanceState )
|
||||||
{
|
{
|
||||||
super.onCreate( savedInstanceState );
|
super.onCreate( savedInstanceState );
|
||||||
DownloadFilesTask dft = null;
|
DownloadFilesTask dft = null;
|
||||||
|
|
||||||
requestWindowFeature( Window.FEATURE_LEFT_ICON );
|
requestWindowFeature( Window.FEATURE_LEFT_ICON );
|
||||||
setContentView( R.layout.import_dict );
|
setContentView( R.layout.import_dict );
|
||||||
getWindow().setFeatureDrawableResource( Window.FEATURE_LEFT_ICON,
|
getWindow().setFeatureDrawableResource( Window.FEATURE_LEFT_ICON,
|
||||||
R.drawable.icon48x48 );
|
R.drawable.icon48x48 );
|
||||||
|
|
||||||
ProgressBar progressBar = (ProgressBar)findViewById( R.id.progress_bar );
|
ProgressBar progressBar = (ProgressBar)findViewById( R.id.progress_bar );
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
Uri uri = intent.getData();
|
Uri uri = intent.getData();
|
||||||
if ( null == uri ) {
|
if ( null == uri ) {
|
||||||
String url = intent.getStringExtra( APK_EXTRA );
|
String url = intent.getStringExtra( APK_EXTRA );
|
||||||
boolean isApp = null != url;
|
boolean isApp = null != url;
|
||||||
|
@ -175,10 +175,10 @@ public class DictImportActivity extends XWActivity {
|
||||||
String msg = getString( R.string.downloading_dictf, showName );
|
String msg = getString( R.string.downloading_dictf, showName );
|
||||||
TextView view = (TextView)findViewById( R.id.dwnld_message );
|
TextView view = (TextView)findViewById( R.id.dwnld_message );
|
||||||
view.setText( msg );
|
view.setText( msg );
|
||||||
|
|
||||||
dft.execute( uri );
|
dft.execute( uri );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private File saveToDownloads( InputStream is, String name )
|
private File saveToDownloads( InputStream is, String name )
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,7 @@ import android.preference.PreferenceManager;
|
||||||
import android.view.ContextMenu.ContextMenuInfo;
|
import android.view.ContextMenu.ContextMenuInfo;
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -44,9 +45,12 @@ import android.widget.Button;
|
||||||
import android.widget.ExpandableListAdapter;
|
import android.widget.ExpandableListAdapter;
|
||||||
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
|
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
|
||||||
import android.widget.ExpandableListView;
|
import android.widget.ExpandableListView;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.eehouse.android.xw4.DictUtils.DictAndLoc;
|
import org.eehouse.android.xw4.DictUtils.DictAndLoc;
|
||||||
|
@ -59,6 +63,11 @@ public class DictsActivity extends XWExpandableListActivity
|
||||||
MountEventReceiver.SDCardNotifiee, DlgDelegate.DlgClickNotify,
|
MountEventReceiver.SDCardNotifiee, DlgDelegate.DlgClickNotify,
|
||||||
DictImportActivity.DownloadFinishedListener {
|
DictImportActivity.DownloadFinishedListener {
|
||||||
|
|
||||||
|
private static interface SafePopup {
|
||||||
|
public void doPopup( Context context, View button, String curDict );
|
||||||
|
}
|
||||||
|
private static SafePopup s_safePopup = null;
|
||||||
|
|
||||||
private static final String DICT_DOLAUNCH = "do_launch";
|
private static final String DICT_DOLAUNCH = "do_launch";
|
||||||
private static final String DICT_LANG_EXTRA = "use_lang";
|
private static final String DICT_LANG_EXTRA = "use_lang";
|
||||||
private static final String DICT_NAME_EXTRA = "use_dict";
|
private static final String DICT_NAME_EXTRA = "use_dict";
|
||||||
|
@ -76,6 +85,11 @@ public class DictsActivity extends XWExpandableListActivity
|
||||||
private static final int MOVE_DICT = DlgDelegate.DIALOG_LAST + 1;
|
private static final int MOVE_DICT = DlgDelegate.DIALOG_LAST + 1;
|
||||||
private static final int SET_DEFAULT = DlgDelegate.DIALOG_LAST + 2;
|
private static final int SET_DEFAULT = DlgDelegate.DIALOG_LAST + 2;
|
||||||
private static final int DICT_OR_DECLINE = DlgDelegate.DIALOG_LAST + 3;
|
private static final int DICT_OR_DECLINE = DlgDelegate.DIALOG_LAST + 3;
|
||||||
|
|
||||||
|
// I can't provide a subclass of MenuItem to hold DictAndLoc, so
|
||||||
|
// settle for a hash on the side.
|
||||||
|
private static HashMap<MenuItem, DictAndLoc> s_itemData;
|
||||||
|
|
||||||
private int m_lang = 0;
|
private int m_lang = 0;
|
||||||
private String[] m_langs;
|
private String[] m_langs;
|
||||||
private String m_name = null;
|
private String m_name = null;
|
||||||
|
@ -779,5 +793,68 @@ public class DictsActivity extends XWExpandableListActivity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class SafePopupImpl implements SafePopup {
|
||||||
|
public void doPopup( final Context context, View button,
|
||||||
|
String curDict ) {
|
||||||
|
|
||||||
|
MenuItem.OnMenuItemClickListener listener =
|
||||||
|
new MenuItem.OnMenuItemClickListener() {
|
||||||
|
public boolean onMenuItemClick( MenuItem item )
|
||||||
|
{
|
||||||
|
DictAndLoc dal = s_itemData.get( item );
|
||||||
|
s_itemData = null;
|
||||||
|
|
||||||
|
if ( null == dal ) {
|
||||||
|
DictsActivity.start( context );
|
||||||
|
} else {
|
||||||
|
DictBrowseActivity.launch( context, dal.name,
|
||||||
|
dal.loc );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
s_itemData = new HashMap<MenuItem, DictAndLoc>();
|
||||||
|
PopupMenu popup = new PopupMenu( context, button );
|
||||||
|
Menu menu = popup.getMenu();
|
||||||
|
menu.add( R.string.show_wordlist_browser )
|
||||||
|
.setOnMenuItemClickListener( listener );
|
||||||
|
|
||||||
|
// Add at top but save until have dal info
|
||||||
|
MenuItem curItem = menu.add( curDict );
|
||||||
|
|
||||||
|
DictAndLoc[] dals = DictUtils.dictList( context );
|
||||||
|
for ( DictAndLoc dal : dals ) {
|
||||||
|
MenuItem item = dal.name.equals(curDict)
|
||||||
|
? curItem : menu.add( dal.name );
|
||||||
|
item.setOnMenuItemClickListener( listener );
|
||||||
|
s_itemData.put( item, dal );
|
||||||
|
}
|
||||||
|
popup.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean handleDictsPopup( Context context, View button,
|
||||||
|
String curDict )
|
||||||
|
{
|
||||||
|
if ( null == s_safePopup ) {
|
||||||
|
int sdkVersion = Integer.valueOf( android.os.Build.VERSION.SDK );
|
||||||
|
if ( 11 <= sdkVersion ) {
|
||||||
|
s_safePopup = new SafePopupImpl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean canHandle = null != s_safePopup;
|
||||||
|
if ( canHandle ) {
|
||||||
|
s_safePopup.doPopup( context, button, curDict );
|
||||||
|
}
|
||||||
|
return canHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void start( Context context )
|
||||||
|
{
|
||||||
|
Intent intent = new Intent( context, DictsActivity.class );
|
||||||
|
context.startActivity( intent );
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -102,7 +102,7 @@ public class DlgDelegate {
|
||||||
|
|
||||||
public Dialog onCreateDialog( int id )
|
public Dialog onCreateDialog( int id )
|
||||||
{
|
{
|
||||||
DbgUtils.logf("onCreateDialog(id=%d)", id );
|
// DbgUtils.logf("onCreateDialog(id=%d)", id );
|
||||||
Dialog dialog = null;
|
Dialog dialog = null;
|
||||||
DlgState state = findForID( id );
|
DlgState state = findForID( id );
|
||||||
switch( id ) {
|
switch( id ) {
|
||||||
|
@ -138,11 +138,6 @@ public class DlgDelegate {
|
||||||
// Assert.assertNull( m_dlgStates );
|
// Assert.assertNull( m_dlgStates );
|
||||||
DlgState state = new DlgState( DIALOG_OKONLY, msg, callbackID );
|
DlgState state = new DlgState( DIALOG_OKONLY, msg, callbackID );
|
||||||
addState( state );
|
addState( state );
|
||||||
// m_msg = msg;
|
|
||||||
// if ( 0 != callbackID ) {
|
|
||||||
// Assert.assertTrue( 0 == m_cbckID );
|
|
||||||
// m_cbckID = callbackID;
|
|
||||||
// }
|
|
||||||
m_activity.showDialog( DIALOG_OKONLY );
|
m_activity.showDialog( DIALOG_OKONLY );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,18 +187,10 @@ public class DlgDelegate {
|
||||||
|
|
||||||
public void showConfirmThen( String msg, int posButton, int callbackID )
|
public void showConfirmThen( String msg, int posButton, int callbackID )
|
||||||
{
|
{
|
||||||
// FIX ME!! Need to store data per message rather than have
|
DlgState state = new DlgState( CONFIRM_THEN, msg, posButton,
|
||||||
// assertions failing or messages dropped.
|
callbackID, 0 );
|
||||||
if ( false /*0 != m_cbckID*/ ) {
|
addState( state );
|
||||||
// DbgUtils.logf( "showConfirmThen: busy with another message; "
|
m_activity.showDialog( CONFIRM_THEN );
|
||||||
// + "dropping \"%s\" in favor of \"%s\"",
|
|
||||||
// msg, m_msg );
|
|
||||||
} else {
|
|
||||||
DlgState state = new DlgState( CONFIRM_THEN, msg, posButton,
|
|
||||||
callbackID, 0 );
|
|
||||||
addState( state );
|
|
||||||
m_activity.showDialog( CONFIRM_THEN );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showEmailOrSMSThen( final int callbackID )
|
public void showEmailOrSMSThen( final int callbackID )
|
||||||
|
@ -454,7 +441,7 @@ public class DlgDelegate {
|
||||||
private DlgState findForID( int id )
|
private DlgState findForID( int id )
|
||||||
{
|
{
|
||||||
DlgState state = m_dlgStates.get( id );
|
DlgState state = m_dlgStates.get( id );
|
||||||
DbgUtils.logf( "findForID(%d)=>%H", id, state );
|
// DbgUtils.logf( "findForID(%d)=>%H", id, state );
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,8 +451,8 @@ public class DlgDelegate {
|
||||||
Assert.assertNotNull( state );
|
Assert.assertNotNull( state );
|
||||||
// Assert.assertTrue( state == m_dlgStates.get( state.m_id ) );
|
// Assert.assertTrue( state == m_dlgStates.get( state.m_id ) );
|
||||||
m_dlgStates.remove( state.m_id );
|
m_dlgStates.remove( state.m_id );
|
||||||
DbgUtils.logf( "dropState: active dialogs now %d from %d ",
|
// DbgUtils.logf( "dropState: active dialogs now %d from %d ",
|
||||||
m_dlgStates.size(), nDlgs );
|
// m_dlgStates.size(), nDlgs );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addState( DlgState state )
|
private void addState( DlgState state )
|
||||||
|
|
|
@ -37,48 +37,48 @@ public class FirstRunDialog {
|
||||||
{
|
{
|
||||||
String page = null;
|
String page = null;
|
||||||
InputStream inputStream = null;
|
InputStream inputStream = null;
|
||||||
try {
|
try {
|
||||||
inputStream = context.getResources()
|
inputStream = context.getResources()
|
||||||
.openRawResource(R.raw.changes);
|
.openRawResource(R.raw.changes);
|
||||||
|
|
||||||
final char[] buf = new char[0x1000];
|
final char[] buf = new char[0x1000];
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
Reader reader = new InputStreamReader( inputStream, "UTF-8" );
|
Reader reader = new InputStreamReader( inputStream, "UTF-8" );
|
||||||
int nRead;
|
int nRead;
|
||||||
do {
|
do {
|
||||||
nRead = reader.read( buf, 0, buf.length );
|
nRead = reader.read( buf, 0, buf.length );
|
||||||
if ( nRead > 0 ) {
|
if ( nRead > 0 ) {
|
||||||
stringBuilder.append( buf, 0, nRead );
|
stringBuilder.append( buf, 0, nRead );
|
||||||
}
|
}
|
||||||
} while ( nRead >= 0 );
|
} while ( nRead >= 0 );
|
||||||
|
|
||||||
page = stringBuilder.toString();
|
page = stringBuilder.toString();
|
||||||
}
|
}
|
||||||
catch ( IOException ioe ) {
|
catch ( IOException ioe ) {
|
||||||
DbgUtils.loge( ioe );
|
DbgUtils.loge( ioe );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
// could just catch NPE....
|
// could just catch NPE....
|
||||||
if ( null != inputStream ) {
|
if ( null != inputStream ) {
|
||||||
try {
|
try {
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
} catch ( IOException ioe ) {
|
} catch ( IOException ioe ) {
|
||||||
DbgUtils.loge( ioe );
|
DbgUtils.loge( ioe );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This won't support e.g mailto refs. Probably want to
|
// This won't support e.g mailto refs. Probably want to
|
||||||
// launch the browser with an intent eventually.
|
// launch the browser with an intent eventually.
|
||||||
WebView view = new WebView( context );
|
WebView view = new WebView( context );
|
||||||
view.loadData( page, "text/html", "utf-8" );
|
view.loadData( page, "text/html", "utf-8" );
|
||||||
|
|
||||||
AlertDialog dialog = new AlertDialog.Builder( context )
|
AlertDialog dialog = new AlertDialog.Builder( context )
|
||||||
.setIcon(android.R.drawable.ic_menu_info_details)
|
.setIcon(android.R.drawable.ic_menu_info_details)
|
||||||
.setTitle( R.string.changes_title )
|
.setTitle( R.string.changes_title )
|
||||||
.setView( view )
|
.setView( view )
|
||||||
.setPositiveButton( R.string.button_ok, null)
|
.setPositiveButton( R.string.button_ok, null)
|
||||||
.create();
|
.create();
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ public class GCMIntentService extends GCMBaseIntentService {
|
||||||
super( GCMConsts.SENDER_ID );
|
super( GCMConsts.SENDER_ID );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onError( Context context, String error )
|
protected void onError( Context context, String error )
|
||||||
{
|
{
|
||||||
DbgUtils.logf("GCMIntentService.onError(%s)", error );
|
DbgUtils.logf("GCMIntentService.onError(%s)", error );
|
||||||
|
|
|
@ -189,12 +189,12 @@ public class GameListItem extends LinearLayout
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String name = GameUtils.getName( m_context, m_rowid );
|
||||||
if ( null != value ) {
|
if ( null != value ) {
|
||||||
String name = GameUtils.getName( m_context, m_rowid );
|
|
||||||
value = m_context.getString( R.string.str_game_namef,
|
value = m_context.getString( R.string.str_game_namef,
|
||||||
name, value );
|
name, value );
|
||||||
} else {
|
} else {
|
||||||
value = GameUtils.getName( m_context, m_rowid );
|
value = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
view.setText( value );
|
view.setText( value );
|
||||||
|
|
|
@ -282,7 +282,7 @@ public class GameUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long saveNewGame( Context context, int gamePtr,
|
public static long saveNewGame( Context context, int gamePtr,
|
||||||
CurGameInfo gi )
|
CurGameInfo gi )
|
||||||
{
|
{
|
||||||
byte[] stream = XwJNI.game_saveToStream( gamePtr, gi );
|
byte[] stream = XwJNI.game_saveToStream( gamePtr, gi );
|
||||||
GameLock lock = DBUtils.saveNewGame( context, stream );
|
GameLock lock = DBUtils.saveNewGame( context, stream );
|
||||||
|
|
|
@ -102,6 +102,7 @@ public class GamesList extends XWExpandableListActivity
|
||||||
private String m_nameField;
|
private String m_nameField;
|
||||||
private NetLaunchInfo m_netLaunchInfo;
|
private NetLaunchInfo m_netLaunchInfo;
|
||||||
private GameNamer m_namer;
|
private GameNamer m_namer;
|
||||||
|
private boolean m_gameLaunched = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dialog onCreateDialog( int id )
|
protected Dialog onCreateDialog( int id )
|
||||||
|
@ -135,7 +136,7 @@ public class GamesList extends XWExpandableListActivity
|
||||||
String message;
|
String message;
|
||||||
String langName =
|
String langName =
|
||||||
DictLangCache.getLangName( this, m_missingDictLang );
|
DictLangCache.getLangName( this, m_missingDictLang );
|
||||||
String gameName = GameUtils.getName( this, m_rowid );
|
String gameName = GameUtils.getName( this, m_missingDictRowId );
|
||||||
if ( WARN_NODICT == id ) {
|
if ( WARN_NODICT == id ) {
|
||||||
message = getString( R.string.no_dictf,
|
message = getString( R.string.no_dictf,
|
||||||
gameName, langName );
|
gameName, langName );
|
||||||
|
@ -385,6 +386,7 @@ public class GamesList extends XWExpandableListActivity
|
||||||
protected void onNewIntent( Intent intent )
|
protected void onNewIntent( Intent intent )
|
||||||
{
|
{
|
||||||
super.onNewIntent( intent );
|
super.onNewIntent( intent );
|
||||||
|
m_gameLaunched = false;
|
||||||
Assert.assertNotNull( intent );
|
Assert.assertNotNull( intent );
|
||||||
invalRelayIDs( intent.getStringArrayExtra( RELAYIDS_EXTRA ) );
|
invalRelayIDs( intent.getStringArrayExtra( RELAYIDS_EXTRA ) );
|
||||||
invalRowID( intent.getLongExtra( ROWID_EXTRA, -1 ) );
|
invalRowID( intent.getLongExtra( ROWID_EXTRA, -1 ) );
|
||||||
|
@ -464,6 +466,7 @@ public class GamesList extends XWExpandableListActivity
|
||||||
super.onWindowFocusChanged( hasFocus );
|
super.onWindowFocusChanged( hasFocus );
|
||||||
if ( hasFocus ) {
|
if ( hasFocus ) {
|
||||||
updateField();
|
updateField();
|
||||||
|
m_gameLaunched = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,21 +490,23 @@ public class GamesList extends XWExpandableListActivity
|
||||||
// We need a way to let the user get back to the basic-config
|
// We need a way to let the user get back to the basic-config
|
||||||
// dialog in case it was dismissed. That way it to check for
|
// dialog in case it was dismissed. That way it to check for
|
||||||
// an empty room name.
|
// an empty room name.
|
||||||
if ( summary.conType == CommsAddrRec.CommsConnType.COMMS_CONN_RELAY
|
if ( !m_gameLaunched ) {
|
||||||
&& summary.roomName.length() == 0 ) {
|
if ( summary.conType == CommsAddrRec.CommsConnType.COMMS_CONN_RELAY
|
||||||
// If it's unconfigured and of the type RelayGameActivity
|
&& summary.roomName.length() == 0 ) {
|
||||||
// can handle send it there, otherwise use the full-on
|
// If it's unconfigured and of the type RelayGameActivity
|
||||||
// config.
|
// can handle send it there, otherwise use the full-on
|
||||||
Class clazz;
|
// config.
|
||||||
if ( RelayGameActivity.isSimpleGame( summary ) ) {
|
Class clazz;
|
||||||
clazz = RelayGameActivity.class;
|
if ( RelayGameActivity.isSimpleGame( summary ) ) {
|
||||||
|
clazz = RelayGameActivity.class;
|
||||||
|
} else {
|
||||||
|
clazz = GameConfig.class;
|
||||||
|
}
|
||||||
|
GameUtils.doConfig( this, rowid, clazz );
|
||||||
} else {
|
} else {
|
||||||
clazz = GameConfig.class;
|
if ( checkWarnNoDict( rowid ) ) {
|
||||||
}
|
launchGame( rowid );
|
||||||
GameUtils.doConfig( this, rowid, clazz );
|
}
|
||||||
} else {
|
|
||||||
if ( checkWarnNoDict( rowid ) ) {
|
|
||||||
launchGame( rowid );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,7 +671,6 @@ public class GamesList extends XWExpandableListActivity
|
||||||
public boolean onOptionsItemSelected( MenuItem item )
|
public boolean onOptionsItemSelected( MenuItem item )
|
||||||
{
|
{
|
||||||
boolean handled = true;
|
boolean handled = true;
|
||||||
Intent intent;
|
|
||||||
|
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.gamel_menu_newgame:
|
case R.id.gamel_menu_newgame:
|
||||||
|
@ -678,8 +682,7 @@ public class GamesList extends XWExpandableListActivity
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case R.id.gamel_menu_dicts:
|
case R.id.gamel_menu_dicts:
|
||||||
intent = new Intent( this, DictsActivity.class );
|
DictsActivity.start( this );
|
||||||
startActivity( intent );
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case R.id.gamel_menu_checkmoves:
|
case R.id.gamel_menu_checkmoves:
|
||||||
|
@ -1089,7 +1092,10 @@ public class GamesList extends XWExpandableListActivity
|
||||||
|
|
||||||
private void launchGame( long rowid, boolean invited )
|
private void launchGame( long rowid, boolean invited )
|
||||||
{
|
{
|
||||||
GameUtils.launchGame( this, rowid, invited );
|
if ( !m_gameLaunched ) {
|
||||||
|
m_gameLaunched = true;
|
||||||
|
GameUtils.launchGame( this, rowid, invited );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launchGame( long rowid )
|
private void launchGame( long rowid )
|
||||||
|
|
|
@ -92,15 +92,29 @@ public class Toolbar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener( int index, View.OnClickListener listener )
|
public ImageButton getViewFor( int index )
|
||||||
{
|
{
|
||||||
TBButtonInfo info = s_buttonInfo[index];
|
TBButtonInfo info = s_buttonInfo[index];
|
||||||
ImageButton button = (ImageButton)m_activity.findViewById( info.m_id );
|
ImageButton button = (ImageButton)m_activity.findViewById( info.m_id );
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setListener( int index, View.OnClickListener listener )
|
||||||
|
{
|
||||||
|
ImageButton button = getViewFor( index );
|
||||||
if ( null != button ) {
|
if ( null != button ) {
|
||||||
button.setOnClickListener( listener );
|
button.setOnClickListener( listener );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setLongClickListener( int index, View.OnLongClickListener listener )
|
||||||
|
{
|
||||||
|
ImageButton button = getViewFor( index );
|
||||||
|
if ( null != button ) {
|
||||||
|
button.setOnLongClickListener( listener );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setListener( int index, final int msgID, final int prefsKey,
|
public void setListener( int index, final int msgID, final int prefsKey,
|
||||||
final int callback )
|
final int callback )
|
||||||
{
|
{
|
||||||
|
@ -112,6 +126,18 @@ public class Toolbar {
|
||||||
setListener( index, listener );
|
setListener( index, listener );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLongClickListener( int index, final int msgID, final int prefsKey,
|
||||||
|
final int callback )
|
||||||
|
{
|
||||||
|
View.OnLongClickListener listener = new View.OnLongClickListener() {
|
||||||
|
public boolean onLongClick( View view ) {
|
||||||
|
m_activity.showNotAgainDlgThen( msgID, prefsKey, callback );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setLongClickListener( index, listener );
|
||||||
|
}
|
||||||
|
|
||||||
public void update( int index, boolean enable )
|
public void update( int index, boolean enable )
|
||||||
{
|
{
|
||||||
TBButtonInfo info = s_buttonInfo[index];
|
TBButtonInfo info = s_buttonInfo[index];
|
||||||
|
|
|
@ -165,7 +165,14 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
|
||||||
String url = String.format( "%s/%s",
|
String url = String.format( "%s/%s",
|
||||||
XWPrefs.getDefaultUpdateUrl( context ),
|
XWPrefs.getDefaultUpdateUrl( context ),
|
||||||
proc );
|
proc );
|
||||||
return new HttpPost( url );
|
HttpPost result;
|
||||||
|
try {
|
||||||
|
result = new HttpPost( url );
|
||||||
|
} catch ( IllegalArgumentException iae ) {
|
||||||
|
DbgUtils.loge( iae );
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String runPost( HttpPost post, JSONObject params )
|
private static String runPost( HttpPost post, JSONObject params )
|
||||||
|
@ -244,7 +251,10 @@ public class UpdateCheckReceiver extends BroadcastReceiver {
|
||||||
@Override protected String doInBackground( Void... unused )
|
@Override protected String doInBackground( Void... unused )
|
||||||
{
|
{
|
||||||
HttpPost post = makePost( m_context, "getUpdates" );
|
HttpPost post = makePost( m_context, "getUpdates" );
|
||||||
String json = runPost( post, m_params );
|
String json = null;
|
||||||
|
if ( null != post ) {
|
||||||
|
json = runPost( post, m_params );
|
||||||
|
}
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ public class Utils {
|
||||||
public static final int TURN_COLOR = 0x7F00FF00;
|
public static final int TURN_COLOR = 0x7F00FF00;
|
||||||
|
|
||||||
private static final String DB_PATH = "XW_GAMES";
|
private static final String DB_PATH = "XW_GAMES";
|
||||||
private static final String HIDDEN_PREFS = "xwprefs_hidden";
|
private static final String HIDDEN_PREFS = "xwprefs_hidden";
|
||||||
private static final String SHOWN_VERSION_KEY = "SHOWN_VERSION_KEY";
|
private static final String SHOWN_VERSION_KEY = "SHOWN_VERSION_KEY";
|
||||||
|
|
||||||
private static Boolean s_isFirstBootThisVersion = null;
|
private static Boolean s_isFirstBootThisVersion = null;
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class XWApp extends Application {
|
||||||
public static final boolean GCMSUPPORTED = true;
|
public static final boolean GCMSUPPORTED = true;
|
||||||
public static final boolean ATTACH_SUPPORTED = true;
|
public static final boolean ATTACH_SUPPORTED = true;
|
||||||
public static final boolean REMATCH_SUPPORTED = false;
|
public static final boolean REMATCH_SUPPORTED = false;
|
||||||
public static final boolean DEBUG = true;
|
public static final boolean DEBUG = false;
|
||||||
public static final boolean DEBUG_LOCKS = false && DEBUG;
|
public static final boolean DEBUG_LOCKS = false && DEBUG;
|
||||||
public static final boolean DEBUG_EXP_TIMERS = false && DEBUG;
|
public static final boolean DEBUG_EXP_TIMERS = false && DEBUG;
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,8 @@ public interface DrawCtx {
|
||||||
int flags );
|
int flags );
|
||||||
void drawTileBack( Rect rect, int flags );
|
void drawTileBack( Rect rect, int flags );
|
||||||
void drawTrayDivider( Rect rect, int flags );
|
void drawTrayDivider( Rect rect, int flags );
|
||||||
void score_pendingScore( Rect rect, int score, int playerNum, int flags );
|
void score_pendingScore( Rect rect, int score, int playerNum, int curTurn,
|
||||||
|
int flags );
|
||||||
|
|
||||||
public static final int BONUS_NONE = 0;
|
public static final int BONUS_NONE = 0;
|
||||||
public static final int BONUS_DOUBLE_LETTER = 1;
|
public static final int BONUS_DOUBLE_LETTER = 1;
|
||||||
|
|
|
@ -8,12 +8,6 @@ usage () {
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
getSDK() {
|
|
||||||
LINE=$(grep 'android:minSdkVersion' ./AndroidManifest.xml)
|
|
||||||
SDK=$(echo $LINE | sed 's/^.*targetSdkVersion=\"\([0-9]*\)\".*$/\1/')
|
|
||||||
echo $SDK
|
|
||||||
}
|
|
||||||
|
|
||||||
TAG=""
|
TAG=""
|
||||||
BRANCH=""
|
BRANCH=""
|
||||||
VARIANT="XWords4"
|
VARIANT="XWords4"
|
||||||
|
@ -67,7 +61,7 @@ git clone $SRCDIR BUILD
|
||||||
cd BUILD
|
cd BUILD
|
||||||
git checkout ${TAG}${BRANCH}
|
git checkout ${TAG}${BRANCH}
|
||||||
cd ./xwords4/android/${VARIANT}
|
cd ./xwords4/android/${VARIANT}
|
||||||
../scripts/setup_local_props.sh --target android-$(getSDK)
|
../scripts/setup_local_props.sh
|
||||||
../scripts/arelease.sh --variant ${VARIANT}
|
../scripts/arelease.sh --variant ${VARIANT}
|
||||||
mkdir -p /tmp/releases_${VARIANT}
|
mkdir -p /tmp/releases_${VARIANT}
|
||||||
cp *.apk /tmp/releases_${VARIANT}
|
cp *.apk /tmp/releases_${VARIANT}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
set -u -e
|
set -u -e
|
||||||
|
|
||||||
TARGET="android-7"
|
TARGET="Google Inc.:Google APIs:11"
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "usage: $0 [--target TARGET]"
|
echo "usage: $0 [--target TARGET]"
|
||||||
|
@ -24,9 +24,8 @@ while [ $# -ge 1 ]; do
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
# create local.properties for 1.6 sdk (target id 4). Use 'android
|
# create local.properties
|
||||||
# list targets' to get the full set.
|
android update project --path . --target "$TARGET"
|
||||||
android update project --path . --target $TARGET
|
|
||||||
|
|
||||||
echo "local.properties looks like this:"
|
echo "local.properties looks like this:"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
|
@ -197,6 +197,7 @@ static void putDevID( const CommsCtxt* comms, XWStreamCtxt* stream );
|
||||||
# endif
|
# endif
|
||||||
# ifdef DEBUG
|
# ifdef DEBUG
|
||||||
static const char* relayCmdToStr( XWRELAY_Cmd cmd );
|
static const char* relayCmdToStr( XWRELAY_Cmd cmd );
|
||||||
|
static void printQueue( const CommsCtxt* comms );
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
#if defined RELAY_HEARTBEAT || defined COMMS_HEARTBEAT
|
#if defined RELAY_HEARTBEAT || defined COMMS_HEARTBEAT
|
||||||
|
@ -287,7 +288,9 @@ static void
|
||||||
init_relay( CommsCtxt* comms, XP_U16 nPlayersHere, XP_U16 nPlayersTotal )
|
init_relay( CommsCtxt* comms, XP_U16 nPlayersHere, XP_U16 nPlayersTotal )
|
||||||
{
|
{
|
||||||
comms->r.myHostID = comms->isServer? HOST_ID_SERVER: HOST_ID_NONE;
|
comms->r.myHostID = comms->isServer? HOST_ID_SERVER: HOST_ID_NONE;
|
||||||
XP_LOGF( "%s: set hostid: %x", __func__, comms->r.myHostID );
|
if ( HOST_ID_NONE != comms->r.myHostID ) {
|
||||||
|
XP_LOGF( "%s: set hostid: %x", __func__, comms->r.myHostID );
|
||||||
|
}
|
||||||
set_relay_state( comms, COMMS_RELAYSTATE_UNCONNECTED );
|
set_relay_state( comms, COMMS_RELAYSTATE_UNCONNECTED );
|
||||||
comms->r.nPlayersHere = nPlayersHere;
|
comms->r.nPlayersHere = nPlayersHere;
|
||||||
comms->r.nPlayersTotal = nPlayersTotal;
|
comms->r.nPlayersTotal = nPlayersTotal;
|
||||||
|
@ -964,6 +967,7 @@ static MsgQueueElem*
|
||||||
makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec,
|
makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec,
|
||||||
XP_PlayerAddr channelNo, XWStreamCtxt* stream )
|
XP_PlayerAddr channelNo, XWStreamCtxt* stream )
|
||||||
{
|
{
|
||||||
|
XP_LOGF( "%s(channelNo=%x)", __func__, channelNo );
|
||||||
XP_U16 headerLen;
|
XP_U16 headerLen;
|
||||||
XP_U16 streamSize = NULL == stream? 0 : stream_getSize( stream );
|
XP_U16 streamSize = NULL == stream? 0 : stream_getSize( stream );
|
||||||
MsgID lastMsgSaved = (!!rec)? rec->lastMsgSaved : 0;
|
MsgID lastMsgSaved = (!!rec)? rec->lastMsgSaved : 0;
|
||||||
|
@ -1016,7 +1020,7 @@ makeElemWithID( CommsCtxt* comms, MsgID msgID, AddressRecord* rec,
|
||||||
XP_U16
|
XP_U16
|
||||||
comms_getChannelSeed( CommsCtxt* comms )
|
comms_getChannelSeed( CommsCtxt* comms )
|
||||||
{
|
{
|
||||||
while ( comms->channelSeed == 0 ) {
|
while ( 0 == (comms->channelSeed & ~CHANNEL_MASK) ) {
|
||||||
comms->channelSeed = XP_RANDOM();
|
comms->channelSeed = XP_RANDOM();
|
||||||
XP_LOGF( "%s: channelSeed: %.4X", __func__, comms->channelSeed );
|
XP_LOGF( "%s: channelSeed: %.4X", __func__, comms->channelSeed );
|
||||||
}
|
}
|
||||||
|
@ -1073,10 +1077,10 @@ addToQueue( CommsCtxt* comms, MsgQueueElem* newMsgElem )
|
||||||
XP_ASSERT( comms->queueLen > 0 );
|
XP_ASSERT( comms->queueLen > 0 );
|
||||||
}
|
}
|
||||||
++comms->queueLen;
|
++comms->queueLen;
|
||||||
XP_LOGF( "%s: queueLen now %d after channelNo: %d; msgID: " XP_LD
|
XP_ASSERT( comms->queueLen <= 128 ); /* reasonable limit in testing */
|
||||||
"; len: %d", __func__, comms->queueLen,
|
#ifdef DEBUG
|
||||||
newMsgElem->channelNo & CHANNEL_MASK, newMsgElem->msgID,
|
printQueue( comms );
|
||||||
newMsgElem->len );
|
#endif
|
||||||
} /* addToQueue */
|
} /* addToQueue */
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -1088,7 +1092,7 @@ printQueue( const CommsCtxt* comms )
|
||||||
|
|
||||||
for ( elem = comms->msgQueueHead, ii = 0; ii < comms->queueLen;
|
for ( elem = comms->msgQueueHead, ii = 0; ii < comms->queueLen;
|
||||||
elem = elem->next, ++ii ) {
|
elem = elem->next, ++ii ) {
|
||||||
XP_STATUSF( "\t%d: channel: %x; msgID=" XP_LD
|
XP_LOGF( "\t%d: channel: %x; msgID=" XP_LD
|
||||||
#ifdef COMMS_CHECKSUM
|
#ifdef COMMS_CHECKSUM
|
||||||
"; check=%s"
|
"; check=%s"
|
||||||
#endif
|
#endif
|
||||||
|
@ -1139,8 +1143,8 @@ freeElem( const CommsCtxt* XP_UNUSED_DBG(comms), MsgQueueElem* elem )
|
||||||
static void
|
static void
|
||||||
removeFromQueue( CommsCtxt* comms, XP_PlayerAddr channelNo, MsgID msgID )
|
removeFromQueue( CommsCtxt* comms, XP_PlayerAddr channelNo, MsgID msgID )
|
||||||
{
|
{
|
||||||
XP_STATUSF( "%s: remove msgs <= " XP_LD " for channel %x (queueLen: %d)",
|
XP_LOGF( "%s: remove msgs <= " XP_LD " for channel %x (queueLen: %d)",
|
||||||
__func__, msgID, channelNo, comms->queueLen );
|
__func__, msgID, channelNo, comms->queueLen );
|
||||||
|
|
||||||
if ( (channelNo == 0) || !!getRecordFor( comms, NULL, channelNo,
|
if ( (channelNo == 0) || !!getRecordFor( comms, NULL, channelNo,
|
||||||
XP_FALSE ) ) {
|
XP_FALSE ) ) {
|
||||||
|
@ -1176,7 +1180,7 @@ removeFromQueue( CommsCtxt* comms, XP_PlayerAddr channelNo, MsgID msgID )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
XP_STATUSF( "%s: queueLen now %d", __func__, comms->queueLen );
|
XP_LOGF( "%s: queueLen now %d", __func__, comms->queueLen );
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
assertQueueOk( comms );
|
assertQueueOk( comms );
|
||||||
|
@ -1191,6 +1195,7 @@ gameID( const CommsCtxt* comms )
|
||||||
if ( 0 == gameID ) {
|
if ( 0 == gameID ) {
|
||||||
gameID = comms->util->gameInfo->gameID;
|
gameID = comms->util->gameInfo->gameID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// XP_ASSERT( 0 != gameID );
|
// XP_ASSERT( 0 != gameID );
|
||||||
if ( 0 == gameID ) {
|
if ( 0 == gameID ) {
|
||||||
XP_LOGF( "%s: gameID STILL 0", __func__ );
|
XP_LOGF( "%s: gameID STILL 0", __func__ );
|
||||||
|
@ -1199,21 +1204,15 @@ gameID( const CommsCtxt* comms )
|
||||||
comms->util->gameInfo->gameID = gameID;
|
comms->util->gameInfo->gameID = gameID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this next is failing on android b/c comms->util->gameInfo->gameID still 0 */
|
|
||||||
#ifdef DEBUG
|
|
||||||
/* if ( (0 != comms->connID) */
|
|
||||||
/* && ((comms->connID & 0xFFFF) */
|
|
||||||
/* != (comms->util->gameInfo->gameID & 0xFFFF)) ) { */
|
|
||||||
/* XP_LOGF("%s: connID: 0X%lX vs gameID: 0X%lX", __func__, comms->connID, */
|
|
||||||
/* comms->util->gameInfo->gameID ); */
|
|
||||||
/* XP_ASSERT(0); */
|
|
||||||
/* } */
|
|
||||||
#endif
|
|
||||||
/* XP_ASSERT( 0 == comms->connID */
|
|
||||||
/* || (comms->connID & 0xFFFF) */
|
|
||||||
/* == (comms->util->gameInfo->gameID & 0xFFFF) ); */
|
|
||||||
/* Most of the time these will be the same, but early in a game they won't
|
/* Most of the time these will be the same, but early in a game they won't
|
||||||
be. Would be nice not to have to use gameID. */
|
be. Would be nice not to have to use gameID. */
|
||||||
|
if ( 0 == gameID ) {
|
||||||
|
XP_LOGF( "%s: gameID STILL 0", __func__ );
|
||||||
|
} else if ( 0 == comms->util->gameInfo->gameID ) {
|
||||||
|
XP_LOGF( "%s: setting gi's gameID to 0X%lX", __func__, gameID );
|
||||||
|
comms->util->gameInfo->gameID = gameID;
|
||||||
|
}
|
||||||
|
|
||||||
return gameID;
|
return gameID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1228,6 +1227,11 @@ sendMsg( CommsCtxt* comms, MsgQueueElem* elem )
|
||||||
|
|
||||||
channelNo = elem->channelNo;
|
channelNo = elem->channelNo;
|
||||||
|
|
||||||
|
#ifdef COMMS_CHECKSUM
|
||||||
|
XP_LOGF( "%s: sending message of len %d with sum %s", __func__, elem->len,
|
||||||
|
elem->checksum );
|
||||||
|
#endif
|
||||||
|
|
||||||
if ( 0 ) {
|
if ( 0 ) {
|
||||||
#ifdef XWFEATURE_RELAY
|
#ifdef XWFEATURE_RELAY
|
||||||
} else if ( conType == COMMS_CONN_RELAY ) {
|
} else if ( conType == COMMS_CONN_RELAY ) {
|
||||||
|
@ -1317,25 +1321,27 @@ comms_resendAll( CommsCtxt* comms, XP_Bool force )
|
||||||
void
|
void
|
||||||
comms_ackAny( CommsCtxt* comms )
|
comms_ackAny( CommsCtxt* comms )
|
||||||
{
|
{
|
||||||
|
if ( CONN_ID_NONE == comms->connID ) {
|
||||||
|
XP_LOGF( "%s: doing nothing because connID still unset", __func__ );
|
||||||
|
} else {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
XP_Bool noneSent = XP_TRUE;
|
XP_U16 nSent = 0;
|
||||||
#endif
|
#endif
|
||||||
AddressRecord* rec;
|
AddressRecord* rec;
|
||||||
for ( rec = comms->recs; !!rec; rec = rec->next ) {
|
for ( rec = comms->recs; !!rec; rec = rec->next ) {
|
||||||
if ( rec->lastMsgAckd < rec->lastMsgRcd ) {
|
if ( rec->lastMsgAckd < rec->lastMsgRcd ) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
noneSent = XP_FALSE;
|
++nSent;
|
||||||
#endif
|
#endif
|
||||||
XP_LOGF( "%s: channel %x; %ld < %ld: rec needs ack", __func__,
|
XP_LOGF( "%s: channel %x; %ld < %ld: rec needs ack", __func__,
|
||||||
rec->channelNo, rec->lastMsgAckd, rec->lastMsgRcd );
|
rec->channelNo, rec->lastMsgAckd, rec->lastMsgRcd );
|
||||||
sendEmptyMsg( comms, rec );
|
sendEmptyMsg( comms, rec );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if ( noneSent ) {
|
XP_LOGF( "%s: sent for %d channels", __func__, nSent );
|
||||||
XP_LOGF( "%s: nothing to send", __func__ );
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1410,7 +1416,8 @@ got_connect_cmd( CommsCtxt* comms, XWStreamCtxt* stream,
|
||||||
__func__, comms->r.connName, connName );
|
__func__, comms->r.connName, connName );
|
||||||
}
|
}
|
||||||
XP_MEMCPY( comms->r.connName, connName, sizeof(comms->r.connName) );
|
XP_MEMCPY( comms->r.connName, connName, sizeof(comms->r.connName) );
|
||||||
XP_LOGF( "%s: connName: \"%s\"", __func__, connName );
|
XP_LOGF( "%s: connName: \"%s\" (reconnect=%d)", __func__, connName,
|
||||||
|
reconnected );
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
stringFromStreamHere( stream, comms->r.connName,
|
stringFromStreamHere( stream, comms->r.connName,
|
||||||
|
@ -1460,8 +1467,11 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID )
|
||||||
|
|
||||||
case XWRELAY_ALLHERE:
|
case XWRELAY_ALLHERE:
|
||||||
srcID = (XWHostID)stream_getU8( stream );
|
srcID = (XWHostID)stream_getU8( stream );
|
||||||
XP_ASSERT( comms->r.myHostID == HOST_ID_NONE
|
if ( comms->r.myHostID != HOST_ID_NONE
|
||||||
|| comms->r.myHostID == srcID );
|
&& comms->r.myHostID != srcID ) {
|
||||||
|
XP_LOGF( "%s: changing hostid from %d to %d", __func__,
|
||||||
|
comms->r.myHostID, srcID );
|
||||||
|
}
|
||||||
|
|
||||||
if ( 0 == comms->r.cookieID ) {
|
if ( 0 == comms->r.cookieID ) {
|
||||||
XP_LOGF( "%s: cookieID still 0; background send?",
|
XP_LOGF( "%s: cookieID still 0; background send?",
|
||||||
|
@ -1649,7 +1659,7 @@ preProcess( CommsCtxt* comms, XWStreamCtxt* stream,
|
||||||
|
|
||||||
static AddressRecord*
|
static AddressRecord*
|
||||||
getRecordFor( CommsCtxt* comms, const CommsAddrRec* addr,
|
getRecordFor( CommsCtxt* comms, const CommsAddrRec* addr,
|
||||||
XP_PlayerAddr channelNo, XP_Bool maskChannel )
|
const XP_PlayerAddr channelNo, XP_Bool maskChannel )
|
||||||
{
|
{
|
||||||
CommsConnType conType;
|
CommsConnType conType;
|
||||||
AddressRecord* rec;
|
AddressRecord* rec;
|
||||||
|
@ -1703,6 +1713,8 @@ getRecordFor( CommsCtxt* comms, const CommsAddrRec* addr,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
XP_LOGF( "%s(channelNo=%x, maskChannel=%s) => %p", __func__,
|
||||||
|
channelNo, maskChannel? "true":"false", rec );
|
||||||
return rec;
|
return rec;
|
||||||
} /* getRecordFor */
|
} /* getRecordFor */
|
||||||
|
|
||||||
|
@ -1756,7 +1768,7 @@ validateInitialMessage( CommsCtxt* comms,
|
||||||
if ( addRec ) {
|
if ( addRec ) {
|
||||||
if ( comms->isServer ) {
|
if ( comms->isServer ) {
|
||||||
XP_LOGF( "%s: looking at channelNo: %x", __func__, *channelNo );
|
XP_LOGF( "%s: looking at channelNo: %x", __func__, *channelNo );
|
||||||
XP_ASSERT( (*channelNo && CHANNEL_MASK) == 0 );
|
XP_ASSERT( (*channelNo & CHANNEL_MASK) == 0 );
|
||||||
*channelNo |= ++comms->nextChannelNo;
|
*channelNo |= ++comms->nextChannelNo;
|
||||||
XP_ASSERT( comms->nextChannelNo <= CHANNEL_MASK );
|
XP_ASSERT( comms->nextChannelNo <= CHANNEL_MASK );
|
||||||
}
|
}
|
||||||
|
@ -1778,12 +1790,22 @@ validateInitialMessage( CommsCtxt* comms,
|
||||||
} else {
|
} else {
|
||||||
if ( comms->isServer ) {
|
if ( comms->isServer ) {
|
||||||
XP_ASSERT( (*channelNo & CHANNEL_MASK) == 0 );
|
XP_ASSERT( (*channelNo & CHANNEL_MASK) == 0 );
|
||||||
*channelNo |= ++comms->nextChannelNo;
|
if ( 0 == (*channelNo & CHANNEL_MASK) ) {
|
||||||
XP_ASSERT( comms->nextChannelNo <= CHANNEL_MASK );
|
*channelNo |= ++comms->nextChannelNo;
|
||||||
|
XP_ASSERT( comms->nextChannelNo <= CHANNEL_MASK );
|
||||||
|
} else {
|
||||||
|
/* Why do I sometimes see these in the middle of a game
|
||||||
|
with lots of messages already sent? connID of 0 should
|
||||||
|
only happen at the start! */
|
||||||
|
XP_LOGF( "%s: dropping msg because channel already set",
|
||||||
|
__func__ );
|
||||||
|
goto errExit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rec = rememberChannelAddress( comms, *channelNo, senderID, addr );
|
rec = rememberChannelAddress( comms, *channelNo, senderID, addr );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
errExit:
|
||||||
LOG_RETURNF( XP_P, rec );
|
LOG_RETURNF( XP_P, rec );
|
||||||
return rec;
|
return rec;
|
||||||
} /* validateInitialMessage */
|
} /* validateInitialMessage */
|
||||||
|
@ -1829,6 +1851,9 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
|
||||||
XP_Bool usingRelay = XP_FALSE;
|
XP_Bool usingRelay = XP_FALSE;
|
||||||
|
|
||||||
XP_ASSERT( retAddr == NULL || comms->addr.conType == retAddr->conType );
|
XP_ASSERT( retAddr == NULL || comms->addr.conType == retAddr->conType );
|
||||||
|
#ifdef COMMS_CHECKSUM
|
||||||
|
XP_U16 initialLen = stream_getSize( stream );
|
||||||
|
#endif
|
||||||
|
|
||||||
if ( !preProcess( comms, stream, &usingRelay, &senderID ) ) {
|
if ( !preProcess( comms, stream, &usingRelay, &senderID ) ) {
|
||||||
XP_U32 connID;
|
XP_U32 connID;
|
||||||
|
@ -1836,6 +1861,17 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
|
||||||
MsgID msgID;
|
MsgID msgID;
|
||||||
MsgID lastMsgRcd;
|
MsgID lastMsgRcd;
|
||||||
|
|
||||||
|
#ifdef COMMS_CHECKSUM
|
||||||
|
{
|
||||||
|
XP_U16 len = stream_getSize( stream );
|
||||||
|
// stream_getPtr pts at base, but sum excludes relay header
|
||||||
|
const XP_U8* ptr = initialLen - len + stream_getPtr( stream );
|
||||||
|
gchar* sum = g_compute_checksum_for_data( G_CHECKSUM_MD5, ptr, len );
|
||||||
|
XP_LOGF( "%s: got message of len %d with sum %s", __func__, len, sum );
|
||||||
|
g_free( sum );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* reject too-small message */
|
/* reject too-small message */
|
||||||
if ( stream_getSize( stream ) >=
|
if ( stream_getSize( stream ) >=
|
||||||
(sizeof(connID) + sizeof(channelNo)
|
(sizeof(connID) + sizeof(channelNo)
|
||||||
|
@ -1861,6 +1897,8 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
|
||||||
} else if ( comms->connID == connID ) {
|
} else if ( comms->connID == connID ) {
|
||||||
rec = validateChannelMessage( comms, retAddr, channelNo, msgID,
|
rec = validateChannelMessage( comms, retAddr, channelNo, msgID,
|
||||||
lastMsgRcd );
|
lastMsgRcd );
|
||||||
|
} else {
|
||||||
|
XP_LOGF( "%s: unexpected connID; dropping message", __func__ );
|
||||||
}
|
}
|
||||||
|
|
||||||
messageValid = (NULL != rec)
|
messageValid = (NULL != rec)
|
||||||
|
|
|
@ -165,6 +165,7 @@ typedef struct DrawCtxVTable {
|
||||||
const XP_Rect* rect,
|
const XP_Rect* rect,
|
||||||
XP_S16 score,
|
XP_S16 score,
|
||||||
XP_U16 playerNum,
|
XP_U16 playerNum,
|
||||||
|
XP_S16 curTurn,
|
||||||
CellFlags flags );
|
CellFlags flags );
|
||||||
|
|
||||||
void DRAW_VTABLE_NAME(drawTimer) ( DrawCtx* dctx, const XP_Rect* rect,
|
void DRAW_VTABLE_NAME(drawTimer) ( DrawCtx* dctx, const XP_Rect* rect,
|
||||||
|
@ -295,8 +296,8 @@ struct DrawCtx {
|
||||||
# define draw_score_drawPlayer(dc, ri, ro, gp, dsi) \
|
# define draw_score_drawPlayer(dc, ri, ro, gp, dsi) \
|
||||||
CALL_DRAW_NAME4(score_drawPlayer,(dc),(ri),(ro),(gp),(dsi))
|
CALL_DRAW_NAME4(score_drawPlayer,(dc),(ri),(ro),(gp),(dsi))
|
||||||
#endif
|
#endif
|
||||||
#define draw_score_pendingScore(dc, r, s, p, f ) \
|
#define draw_score_pendingScore(dc, r, s, p, t, f ) \
|
||||||
CALL_DRAW_NAME4(score_pendingScore,(dc), (r), (s), (p), (f))
|
CALL_DRAW_NAME5(score_pendingScore,(dc), (r), (s), (p), (t), (f))
|
||||||
#define draw_drawTimer( dc, r, plyr, sec ) \
|
#define draw_drawTimer( dc, r, plyr, sec ) \
|
||||||
CALL_DRAW_NAME3(drawTimer,(dc),(r),(plyr),(sec))
|
CALL_DRAW_NAME3(drawTimer,(dc),(r),(plyr),(sec))
|
||||||
#define draw_drawCell( dc, rect, txt, bmap, t, v,o, bon, hi, f ) \
|
#define draw_drawCell( dc, rect, txt, bmap, t, v,o, bon, hi, f ) \
|
||||||
|
|
|
@ -169,7 +169,6 @@ getStateStr( XW_State st )
|
||||||
CASESTR(XWSTATE_NONE);
|
CASESTR(XWSTATE_NONE);
|
||||||
CASESTR(XWSTATE_BEGIN);
|
CASESTR(XWSTATE_BEGIN);
|
||||||
CASESTR(XWSTATE_NEED_SHOWSCORE);
|
CASESTR(XWSTATE_NEED_SHOWSCORE);
|
||||||
CASESTR(XWSTATE_WAITING_ALL_REG);
|
|
||||||
CASESTR(XWSTATE_RECEIVED_ALL_REG);
|
CASESTR(XWSTATE_RECEIVED_ALL_REG);
|
||||||
CASESTR(XWSTATE_NEEDSEND_BADWORD_INFO);
|
CASESTR(XWSTATE_NEEDSEND_BADWORD_INFO);
|
||||||
CASESTR(XWSTATE_MOVE_CONFIRM_WAIT);
|
CASESTR(XWSTATE_MOVE_CONFIRM_WAIT);
|
||||||
|
@ -1171,13 +1170,17 @@ registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream )
|
||||||
RemoteAddress* addr;
|
RemoteAddress* addr;
|
||||||
addr = &server->nv.addresses[server->nv.nDevices];
|
addr = &server->nv.addresses[server->nv.nDevices];
|
||||||
|
|
||||||
deviceIndex = server->nv.nDevices++;
|
|
||||||
|
|
||||||
XP_ASSERT( channelNo != 0 );
|
XP_ASSERT( channelNo != 0 );
|
||||||
addr->channelNo = channelNo;
|
addr->channelNo = channelNo;
|
||||||
|
XP_LOGF( "%s: set channelNo to %x for device %d", __func__,
|
||||||
|
channelNo, server->nv.nDevices );
|
||||||
|
|
||||||
|
deviceIndex = server->nv.nDevices++;
|
||||||
#ifdef STREAM_VERS_BIGBOARD
|
#ifdef STREAM_VERS_BIGBOARD
|
||||||
addr->streamVersion = STREAM_SAVE_PREVWORDS;
|
addr->streamVersion = STREAM_SAVE_PREVWORDS;
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
XP_LOGF( "%s: deviceIndex already set", __func__ );
|
||||||
}
|
}
|
||||||
|
|
||||||
player->deviceIndex = deviceIndex;
|
player->deviceIndex = deviceIndex;
|
||||||
|
@ -1272,11 +1275,12 @@ client_readInitialMessage( ServerCtxt* server, XWStreamCtxt* stream )
|
||||||
channelNo = stream_getAddress( stream );
|
channelNo = stream_getAddress( stream );
|
||||||
XP_ASSERT( channelNo != 0 );
|
XP_ASSERT( channelNo != 0 );
|
||||||
server->nv.addresses[0].channelNo = channelNo;
|
server->nv.addresses[0].channelNo = channelNo;
|
||||||
|
XP_LOGF( "%s: assigning channelNo %x for 0", __func__, channelNo );
|
||||||
|
|
||||||
model_setSize( model, nCols );
|
model_setSize( model, nCols );
|
||||||
|
|
||||||
nPlayers = localGI.nPlayers;
|
nPlayers = localGI.nPlayers;
|
||||||
XP_STATUSF( "reading in %d players", localGI.nPlayers );
|
XP_LOGF( "%s: reading in %d players", __func__, localGI.nPlayers );
|
||||||
|
|
||||||
gi_disposePlayerInfo( MPPARM(server->mpool) &localGI );
|
gi_disposePlayerInfo( MPPARM(server->mpool) &localGI );
|
||||||
|
|
||||||
|
@ -3037,10 +3041,9 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
||||||
{
|
{
|
||||||
ScoresArray scores;
|
ScoresArray scores;
|
||||||
ScoresArray tilePenalties;
|
ScoresArray tilePenalties;
|
||||||
XP_U16 place, nPlayers;
|
XP_U16 place;
|
||||||
XP_S16 quitter = server->nv.quitter;
|
XP_S16 quitter = server->nv.quitter;
|
||||||
XP_Bool quitterDone = XP_FALSE;
|
XP_Bool quitterDone = XP_FALSE;
|
||||||
XP_USE(quitter);
|
|
||||||
ModelCtxt* model = server->vol.model;
|
ModelCtxt* model = server->vol.model;
|
||||||
const XP_UCHAR* addString = util_getUserString( server->vol.util,
|
const XP_UCHAR* addString = util_getUserString( server->vol.util,
|
||||||
STRD_REMAINING_TILES_ADD );
|
STRD_REMAINING_TILES_ADD );
|
||||||
|
@ -3048,18 +3051,18 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
||||||
STRD_UNUSED_TILES_SUB );
|
STRD_UNUSED_TILES_SUB );
|
||||||
XP_UCHAR* timeStr;
|
XP_UCHAR* timeStr;
|
||||||
CurGameInfo* gi = server->vol.gi;
|
CurGameInfo* gi = server->vol.gi;
|
||||||
|
const XP_U16 nPlayers = gi->nPlayers;
|
||||||
|
|
||||||
XP_ASSERT( server->nv.gameState == XWSTATE_GAMEOVER );
|
XP_ASSERT( server->nv.gameState == XWSTATE_GAMEOVER );
|
||||||
|
|
||||||
model_figureFinalScores( model, &scores, &tilePenalties );
|
model_figureFinalScores( model, &scores, &tilePenalties );
|
||||||
|
|
||||||
nPlayers = gi->nPlayers;
|
XP_S16 winningScore = IMPOSSIBLY_LOW_SCORE;
|
||||||
|
|
||||||
for ( place = 1; !quitterDone; ++place ) {
|
for ( place = 1; !quitterDone; ++place ) {
|
||||||
XP_UCHAR timeBuf[16];
|
XP_UCHAR timeBuf[16];
|
||||||
XP_UCHAR buf[128];
|
XP_UCHAR buf[128];
|
||||||
XP_S16 highestScore = IMPOSSIBLY_LOW_SCORE;
|
XP_S16 thisScore = IMPOSSIBLY_LOW_SCORE;
|
||||||
XP_S16 highestIndex = -1;
|
XP_S16 thisIndex = -1;
|
||||||
const XP_UCHAR* placeStr = NULL;
|
const XP_UCHAR* placeStr = NULL;
|
||||||
XP_UCHAR placeBuf[32];
|
XP_UCHAR placeBuf[32];
|
||||||
XP_UCHAR tmpbuf[48];
|
XP_UCHAR tmpbuf[48];
|
||||||
|
@ -3068,22 +3071,27 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
||||||
|
|
||||||
/* Find the next player we should print */
|
/* Find the next player we should print */
|
||||||
for ( ii = 0; ii < nPlayers; ++ii ) {
|
for ( ii = 0; ii < nPlayers; ++ii ) {
|
||||||
if ( quitter != ii && scores.arr[ii] > highestScore ) {
|
if ( quitter != ii && scores.arr[ii] > thisScore ) {
|
||||||
highestIndex = ii;
|
thisIndex = ii;
|
||||||
highestScore = scores.arr[ii];
|
thisScore = scores.arr[ii];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( highestIndex == -1 ) {
|
/* save top score overall to test for winner, including tie case */
|
||||||
|
if ( 1 == place ) {
|
||||||
|
winningScore = thisScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( thisIndex == -1 ) {
|
||||||
if ( quitter >= 0 ) {
|
if ( quitter >= 0 ) {
|
||||||
XP_ASSERT( !quitterDone );
|
XP_ASSERT( !quitterDone );
|
||||||
highestIndex = quitter;
|
thisIndex = quitter;
|
||||||
quitterDone = XP_TRUE;
|
quitterDone = XP_TRUE;
|
||||||
placeKey = STR_RESIGNED;
|
placeKey = STR_RESIGNED;
|
||||||
} else {
|
} else {
|
||||||
break; /* we're done */
|
break; /* we're done */
|
||||||
}
|
}
|
||||||
} else if ( place == 1 ) {
|
} else if ( thisScore == winningScore ) {
|
||||||
placeKey = STR_WINNER;
|
placeKey = STR_WINNER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3098,7 +3106,7 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
||||||
|
|
||||||
timeStr = (XP_UCHAR*)"";
|
timeStr = (XP_UCHAR*)"";
|
||||||
if ( gi->timerEnabled ) {
|
if ( gi->timerEnabled ) {
|
||||||
XP_U16 penalty = player_timePenalty( gi, highestIndex );
|
XP_U16 penalty = player_timePenalty( gi, thisIndex );
|
||||||
if ( penalty > 0 ) {
|
if ( penalty > 0 ) {
|
||||||
XP_SNPRINTF( timeBuf, sizeof(timeBuf),
|
XP_SNPRINTF( timeBuf, sizeof(timeBuf),
|
||||||
util_getUserString(
|
util_getUserString(
|
||||||
|
@ -3109,18 +3117,18 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
firstDone = model_getNumTilesTotal( model, highestIndex) == 0;
|
firstDone = model_getNumTilesTotal( model, thisIndex) == 0;
|
||||||
XP_SNPRINTF( tmpbuf, sizeof(tmpbuf),
|
XP_SNPRINTF( tmpbuf, sizeof(tmpbuf),
|
||||||
(firstDone? addString:subString),
|
(firstDone? addString:subString),
|
||||||
firstDone?
|
firstDone?
|
||||||
tilePenalties.arr[highestIndex]:
|
tilePenalties.arr[thisIndex]:
|
||||||
-tilePenalties.arr[highestIndex] );
|
-tilePenalties.arr[thisIndex] );
|
||||||
|
|
||||||
XP_SNPRINTF( buf, sizeof(buf),
|
XP_SNPRINTF( buf, sizeof(buf),
|
||||||
(XP_UCHAR*)"[%s] %s: %d" XP_CR " (%d %s%s)", placeStr,
|
(XP_UCHAR*)"[%s] %s: %d" XP_CR " (%d %s%s)", placeStr,
|
||||||
emptyStringIfNull(gi->players[highestIndex].name),
|
emptyStringIfNull(gi->players[thisIndex].name),
|
||||||
scores.arr[highestIndex],
|
scores.arr[thisIndex],
|
||||||
model_getPlayerScore( model, highestIndex ),
|
model_getPlayerScore( model, thisIndex ),
|
||||||
tmpbuf, timeStr );
|
tmpbuf, timeStr );
|
||||||
|
|
||||||
if ( 1 < place ) {
|
if ( 1 < place ) {
|
||||||
|
@ -3129,7 +3137,7 @@ server_writeFinalScores( ServerCtxt* server, XWStreamCtxt* stream )
|
||||||
stream_catString( stream, buf );
|
stream_catString( stream, buf );
|
||||||
|
|
||||||
/* Don't consider this one next time around */
|
/* Don't consider this one next time around */
|
||||||
scores.arr[highestIndex] = IMPOSSIBLY_LOW_SCORE;
|
scores.arr[thisIndex] = IMPOSSIBLY_LOW_SCORE;
|
||||||
}
|
}
|
||||||
} /* server_writeFinalScores */
|
} /* server_writeFinalScores */
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ enum {
|
||||||
XWSTATE_BEGIN,
|
XWSTATE_BEGIN,
|
||||||
__UNUSED1, /* was XWSTATE_POOL_INITED */
|
__UNUSED1, /* was XWSTATE_POOL_INITED */
|
||||||
XWSTATE_NEED_SHOWSCORE, /* client-only */
|
XWSTATE_NEED_SHOWSCORE, /* client-only */
|
||||||
XWSTATE_WAITING_ALL_REG, /* includes waiting for dict from server */
|
__XWSTATE_WAITING_ALL_REG, /* unused */
|
||||||
XWSTATE_RECEIVED_ALL_REG, /* includes waiting for dict from server */
|
XWSTATE_RECEIVED_ALL_REG, /* includes waiting for dict from server */
|
||||||
XWSTATE_NEEDSEND_BADWORD_INFO,
|
XWSTATE_NEEDSEND_BADWORD_INFO,
|
||||||
XWSTATE_MOVE_CONFIRM_WAIT, /* client's waiting to hear back */
|
XWSTATE_MOVE_CONFIRM_WAIT, /* client's waiting to hear back */
|
||||||
|
|
|
@ -324,11 +324,12 @@ drawPendingScore( BoardCtxt* board, XP_S16 turnScore, XP_Bool hasCursor )
|
||||||
/* Draw the pending score down in the last tray's rect */
|
/* Draw the pending score down in the last tray's rect */
|
||||||
if ( countTilesToShow( board ) < MAX_TRAY_TILES ) {
|
if ( countTilesToShow( board ) < MAX_TRAY_TILES ) {
|
||||||
XP_U16 selPlayer = board->selPlayer;
|
XP_U16 selPlayer = board->selPlayer;
|
||||||
|
XP_S16 curTurn = server_getCurrentTurn( board->server );
|
||||||
XP_Rect lastTileR;
|
XP_Rect lastTileR;
|
||||||
|
|
||||||
figureTrayTileRect( board, MAX_TRAY_TILES-1, &lastTileR );
|
figureTrayTileRect( board, MAX_TRAY_TILES-1, &lastTileR );
|
||||||
draw_score_pendingScore( board->draw, &lastTileR, turnScore,
|
draw_score_pendingScore( board->draw, &lastTileR, turnScore,
|
||||||
selPlayer,
|
selPlayer, curTurn,
|
||||||
hasCursor?CELL_ISCURSOR:CELL_NONE );
|
hasCursor?CELL_ISCURSOR:CELL_NONE );
|
||||||
}
|
}
|
||||||
} /* drawPendingScore */
|
} /* drawPendingScore */
|
||||||
|
|
|
@ -283,6 +283,7 @@ curses_draw_score_drawPlayer( DrawCtx* p_dctx, const XP_Rect* rInner,
|
||||||
static void
|
static void
|
||||||
curses_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect,
|
curses_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect,
|
||||||
XP_S16 score, XP_U16 XP_UNUSED(playerNum),
|
XP_S16 score, XP_U16 XP_UNUSED(playerNum),
|
||||||
|
XP_S16 XP_UNUSED(curTurn),
|
||||||
CellFlags XP_UNUSED(flags) )
|
CellFlags XP_UNUSED(flags) )
|
||||||
{
|
{
|
||||||
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
|
CursesDrawCtx* dctx = (CursesDrawCtx*)p_dctx;
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <netdb.h> /* gethostbyname */
|
#include <netdb.h> /* gethostbyname */
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -971,7 +973,9 @@ SIGWINCH_handler( int signal )
|
||||||
static void
|
static void
|
||||||
SIGINTTERM_handler( int XP_UNUSED(signal) )
|
SIGINTTERM_handler( int XP_UNUSED(signal) )
|
||||||
{
|
{
|
||||||
write( g_globals.quitPipe[1], "0", 1 );
|
if ( 1 != write( g_globals.quitpipe[1], "!", 1 ) ) {
|
||||||
|
XP_ASSERT(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1133,6 +1137,14 @@ curses_onGameSaved( void* closure, sqlite3_int64 rowid,
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_GLIBLOOP
|
#ifdef USE_GLIBLOOP
|
||||||
|
static gboolean
|
||||||
|
handle_quitwrite( GIOChannel* XP_UNUSED(source), GIOCondition XP_UNUSED(condition), gpointer data )
|
||||||
|
{
|
||||||
|
CursesAppGlobals* globals = (CursesAppGlobals*)data;
|
||||||
|
handleQuit( globals );
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
fire_acceptor( GIOChannel* source, GIOCondition condition, gpointer data )
|
fire_acceptor( GIOChannel* source, GIOCondition condition, gpointer data )
|
||||||
{
|
{
|
||||||
|
@ -1523,6 +1535,18 @@ curses_util_makeStreamFromAddr(XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
||||||
} /* curses_util_makeStreamFromAddr */
|
} /* curses_util_makeStreamFromAddr */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef XWFEATURE_CHAT
|
||||||
|
static void
|
||||||
|
curses_util_showChat( XW_UtilCtxt* uc,
|
||||||
|
const XP_UCHAR* const XP_UNUSED_DBG(msg) )
|
||||||
|
{
|
||||||
|
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
|
||||||
|
globals->nChatsSent = 0;
|
||||||
|
XP_LOGF( "%s: got \"%s\"", __func__, msg );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
|
setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
|
||||||
{
|
{
|
||||||
|
@ -1536,6 +1560,10 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
|
||||||
#ifndef XWFEATURE_STANDALONE_ONLY
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
||||||
util->vtable->m_util_makeStreamFromAddr = curses_util_makeStreamFromAddr;
|
util->vtable->m_util_makeStreamFromAddr = curses_util_makeStreamFromAddr;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef XWFEATURE_CHAT
|
||||||
|
util->vtable->m_util_showChat = curses_util_showChat;
|
||||||
|
#endif
|
||||||
|
|
||||||
util->vtable->m_util_userQuery = curses_util_userQuery;
|
util->vtable->m_util_userQuery = curses_util_userQuery;
|
||||||
util->vtable->m_util_confirmTrade = curses_util_confirmTrade;
|
util->vtable->m_util_confirmTrade = curses_util_confirmTrade;
|
||||||
util->vtable->m_util_userPickTileBlank = curses_util_userPickTileBlank;
|
util->vtable->m_util_userPickTileBlank = curses_util_userPickTileBlank;
|
||||||
|
@ -1658,9 +1686,10 @@ relay_sendNoConn_curses( const XP_U8* msg, XP_U16 len,
|
||||||
} /* relay_sendNoConn_curses */
|
} /* relay_sendNoConn_curses */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
relay_status_curses( void* XP_UNUSED(closure),
|
relay_status_curses( void* closure, CommsRelayState state )
|
||||||
CommsRelayState XP_UNUSED_DBG(state) )
|
|
||||||
{
|
{
|
||||||
|
CursesAppGlobals* globals = (CursesAppGlobals*)closure;
|
||||||
|
globals->commsRelayState = state;
|
||||||
XP_LOGF( "%s got status: %s", __func__, CommsRelayState2Str(state) );
|
XP_LOGF( "%s got status: %s", __func__, CommsRelayState2Str(state) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1670,7 +1699,8 @@ relay_connd_curses( void* XP_UNUSED(closure), XP_UCHAR* const XP_UNUSED(room),
|
||||||
XP_Bool XP_UNUSED_DBG(allHere),
|
XP_Bool XP_UNUSED_DBG(allHere),
|
||||||
XP_U16 XP_UNUSED_DBG(nMissing) )
|
XP_U16 XP_UNUSED_DBG(nMissing) )
|
||||||
{
|
{
|
||||||
XP_LOGF( "%s got allHere: %d; nMissing: %d", __func__, allHere, nMissing );
|
XP_LOGF( "%s got allHere: %s; nMissing: %d", __func__,
|
||||||
|
allHere?"true":"false", nMissing );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1833,6 +1863,31 @@ cursesUDPSocketChanged( void* closure, int newSock, int XP_UNUSED(oldSock),
|
||||||
globals->sources = g_list_append( globals->sources, sd );
|
globals->sources = g_list_append( globals->sources, sd );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
chatsTimerFired( gpointer data )
|
||||||
|
{
|
||||||
|
CursesAppGlobals* globals = (CursesAppGlobals*)data;
|
||||||
|
|
||||||
|
if ( COMMS_RELAYSTATE_ALLCONNECTED == globals->commsRelayState
|
||||||
|
&& 3 > globals->nChatsSent ) {
|
||||||
|
XP_UCHAR msg[128];
|
||||||
|
struct tm* timp;
|
||||||
|
struct timeval tv;
|
||||||
|
struct timezone tz;
|
||||||
|
|
||||||
|
gettimeofday( &tv, &tz );
|
||||||
|
timp = localtime( &tv.tv_sec );
|
||||||
|
|
||||||
|
snprintf( msg, sizeof(msg), "Saying hi via chat at %.2d:%.2d:%.2d",
|
||||||
|
timp->tm_hour, timp->tm_min, timp->tm_sec );
|
||||||
|
XP_LOGF( "%s: sending \"%s\"", __func__, msg );
|
||||||
|
server_sendChat( globals->cGlobals.game.server, msg );
|
||||||
|
++globals->nChatsSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cursesmain( XP_Bool isServer, LaunchParams* params )
|
cursesmain( XP_Bool isServer, LaunchParams* params )
|
||||||
{
|
{
|
||||||
|
@ -1884,12 +1939,18 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_GLIBLOOP
|
#ifdef USE_GLIBLOOP
|
||||||
cursesListenOnSocket( &g_globals, 0, handle_stdin );
|
if ( !params->closeStdin ) {
|
||||||
|
cursesListenOnSocket( &g_globals, 0, handle_stdin );
|
||||||
|
}
|
||||||
setOneSecondTimer( &g_globals.cGlobals );
|
setOneSecondTimer( &g_globals.cGlobals );
|
||||||
|
|
||||||
int result = pipe( g_globals.quitPipe );
|
# ifdef DEBUG
|
||||||
assert( 0 == result );
|
int piperesult =
|
||||||
cursesListenOnSocket( &g_globals, g_globals.quitPipe[0], read_quit );
|
# endif
|
||||||
|
pipe( g_globals.quitpipe );
|
||||||
|
XP_ASSERT( piperesult == 0 );
|
||||||
|
cursesListenOnSocket( &g_globals, g_globals.quitpipe[0], handle_quitwrite );
|
||||||
|
|
||||||
#else
|
#else
|
||||||
cursesListenOnSocket( &g_globals, 0 ); /* stdin */
|
cursesListenOnSocket( &g_globals, 0 ); /* stdin */
|
||||||
|
|
||||||
|
@ -1927,6 +1988,11 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
|
||||||
} else if ( !!params->nbs && !!params->fileName ) {
|
} else if ( !!params->nbs && !!params->fileName ) {
|
||||||
do_nbs_then_close( &g_globals.cGlobals, &procs );
|
do_nbs_then_close( &g_globals.cGlobals, &procs );
|
||||||
} else {
|
} else {
|
||||||
|
if ( 0 != params->chatsInterval ) {
|
||||||
|
(void)g_timeout_add_seconds( params->chatsInterval, chatsTimerFired,
|
||||||
|
&g_globals );
|
||||||
|
}
|
||||||
|
|
||||||
XP_Bool opened = XP_FALSE;
|
XP_Bool opened = XP_FALSE;
|
||||||
initCurses( &g_globals );
|
initCurses( &g_globals );
|
||||||
getmaxyx( g_globals.boardWin, height, width );
|
getmaxyx( g_globals.boardWin, height, width );
|
||||||
|
|
|
@ -70,6 +70,8 @@ struct CursesAppGlobals {
|
||||||
XP_U16 nLinesMenu;
|
XP_U16 nLinesMenu;
|
||||||
gchar* lastErr;
|
gchar* lastErr;
|
||||||
|
|
||||||
|
XP_U16 nChatsSent;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
XWStreamCtxt* stream; /* how we can reach the server */
|
XWStreamCtxt* stream; /* how we can reach the server */
|
||||||
|
@ -82,12 +84,13 @@ struct CursesAppGlobals {
|
||||||
|
|
||||||
short statusLine;
|
short statusLine;
|
||||||
XWGameState state;
|
XWGameState state;
|
||||||
|
CommsRelayState commsRelayState;
|
||||||
|
|
||||||
struct sockaddr_in listenerSockAddr;
|
struct sockaddr_in listenerSockAddr;
|
||||||
#ifdef USE_GLIBLOOP
|
#ifdef USE_GLIBLOOP
|
||||||
GMainLoop* loop;
|
GMainLoop* loop;
|
||||||
GList* sources;
|
GList* sources;
|
||||||
int quitPipe[2];
|
int quitpipe[2];
|
||||||
#else
|
#else
|
||||||
XP_Bool timeToExit;
|
XP_Bool timeToExit;
|
||||||
short fdCount;
|
short fdCount;
|
||||||
|
|
|
@ -1124,8 +1124,8 @@ gtk_draw_measureScoreText( DrawCtx* p_dctx, const XP_Rect* bounds,
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect,
|
gtk_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect,
|
||||||
XP_S16 score, XP_U16 XP_UNUSED(playerNum),
|
XP_S16 score, XP_U16 playerNum,
|
||||||
CellFlags flags )
|
XP_S16 curTurn, CellFlags flags )
|
||||||
{
|
{
|
||||||
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
|
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
|
||||||
XP_UCHAR buf[5];
|
XP_UCHAR buf[5];
|
||||||
|
@ -1133,6 +1133,7 @@ gtk_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect,
|
||||||
XP_Rect localR;
|
XP_Rect localR;
|
||||||
GdkColor* cursor = ((flags & CELL_ISCURSOR) != 0)
|
GdkColor* cursor = ((flags & CELL_ISCURSOR) != 0)
|
||||||
? &dctx->cursor : NULL;
|
? &dctx->cursor : NULL;
|
||||||
|
GdkColor* txtColor;
|
||||||
|
|
||||||
if ( score >= 0 ) {
|
if ( score >= 0 ) {
|
||||||
XP_SNPRINTF( buf, VSIZE(buf), "%.3d", score );
|
XP_SNPRINTF( buf, VSIZE(buf), "%.3d", score );
|
||||||
|
@ -1152,12 +1153,11 @@ gtk_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
ht = localR.height >> 2;
|
ht = localR.height >> 2;
|
||||||
|
txtColor = (playerNum == curTurn) ? &dctx->black : &dctx->grey;
|
||||||
draw_string_at( dctx, NULL, "Pts:", ht,
|
draw_string_at( dctx, NULL, "Pts:", ht,
|
||||||
&localR, XP_GTK_JUST_TOPLEFT,
|
&localR, XP_GTK_JUST_TOPLEFT, txtColor, cursor );
|
||||||
&dctx->black, cursor );
|
|
||||||
draw_string_at( dctx, NULL, buf, ht,
|
draw_string_at( dctx, NULL, buf, ht,
|
||||||
&localR, XP_GTK_JUST_BOTTOMRIGHT,
|
&localR, XP_GTK_JUST_BOTTOMRIGHT, txtColor, cursor );
|
||||||
&dctx->black, cursor );
|
|
||||||
|
|
||||||
} /* gtk_draw_score_pendingScore */
|
} /* gtk_draw_score_pendingScore */
|
||||||
|
|
||||||
|
@ -1375,11 +1375,6 @@ gtkDrawCtxtMake( GtkWidget* drawing_area, GtkGameGlobals* globals )
|
||||||
dctx->drawing_area = drawing_area;
|
dctx->drawing_area = drawing_area;
|
||||||
dctx->globals = globals;
|
dctx->globals = globals;
|
||||||
|
|
||||||
map = gdk_colormap_get_system();
|
|
||||||
|
|
||||||
allocAndSet( map, &dctx->black, 0x0000, 0x0000, 0x0000 );
|
|
||||||
allocAndSet( map, &dctx->white, 0xFFFF, 0xFFFF, 0xFFFF );
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// GdkWindow *window = NULL;
|
// GdkWindow *window = NULL;
|
||||||
/* if ( GTK_WIDGET_FLAGS(GTK_WIDGET(drawing_area)) & GTK_NO_WINDOW ) { */
|
/* if ( GTK_WIDGET_FLAGS(GTK_WIDGET(drawing_area)) & GTK_NO_WINDOW ) { */
|
||||||
|
@ -1406,6 +1401,7 @@ gtkDrawCtxtMake( GtkWidget* drawing_area, GtkGameGlobals* globals )
|
||||||
map = gdk_colormap_get_system();
|
map = gdk_colormap_get_system();
|
||||||
|
|
||||||
allocAndSet( map, &dctx->black, 0x0000, 0x0000, 0x0000 );
|
allocAndSet( map, &dctx->black, 0x0000, 0x0000, 0x0000 );
|
||||||
|
allocAndSet( map, &dctx->grey, 0x7FFF, 0x7FFF, 0x7FFF );
|
||||||
allocAndSet( map, &dctx->white, 0xFFFF, 0xFFFF, 0xFFFF );
|
allocAndSet( map, &dctx->white, 0xFFFF, 0xFFFF, 0xFFFF );
|
||||||
|
|
||||||
allocAndSet( map, &dctx->bonusColors[0], 0xFFFF, 0xAFFF, 0xAFFF );
|
allocAndSet( map, &dctx->bonusColors[0], 0xFFFF, 0xAFFF, 0xAFFF );
|
||||||
|
|
|
@ -383,15 +383,55 @@ static void
|
||||||
gtkSocketChanged( void* closure, int newSock, int XP_UNUSED(oldSock),
|
gtkSocketChanged( void* closure, int newSock, int XP_UNUSED(oldSock),
|
||||||
SockReceiver proc, void* procClosure )
|
SockReceiver proc, void* procClosure )
|
||||||
{
|
{
|
||||||
GtkAppGlobals* apg = (GtkAppGlobals*)closure;
|
/* GtkAppGlobals* apg = (GtkAppGlobals*)closure; */
|
||||||
SourceData* sd = g_malloc( sizeof(*sd) );
|
/* SourceData* sd = g_malloc( sizeof(*sd) ); */
|
||||||
sd->channel = g_io_channel_unix_new( newSock );
|
/* sd->channel = g_io_channel_unix_new( newSock ); */
|
||||||
sd->watch = g_io_add_watch( sd->channel, G_IO_IN | G_IO_ERR,
|
/* sd->watch = g_io_add_watch( sd->channel, G_IO_IN | G_IO_ERR, */
|
||||||
gtk_app_socket_proc, apg );
|
/* gtk_app_socket_proc, apg ); */
|
||||||
sd->proc = proc;
|
/* sd->proc = proc; */
|
||||||
sd->procClosure = procClosure;
|
/* sd->procClosure = procClosure; */
|
||||||
apg->sources = g_list_append( apg->sources, sd );
|
/* apg->sources = g_list_append( apg->sources, sd ); */
|
||||||
}
|
|
||||||
|
GtkAppGlobals* globals = (GtkAppGlobals*)closure;
|
||||||
|
SockInfo* info = (SockInfo*)*storage;
|
||||||
|
XP_LOGF( "%s(old:%d; new:%d)", __func__, oldSock, newSock );
|
||||||
|
|
||||||
|
if ( oldSock != -1 ) {
|
||||||
|
XP_ASSERT( info != NULL );
|
||||||
|
g_source_remove( info->watch );
|
||||||
|
g_io_channel_unref( info->channel );
|
||||||
|
XP_FREE( globals->cGlobals.params->util->mpool, info );
|
||||||
|
*storage = NULL;
|
||||||
|
XP_LOGF( "Removed socket %d from gtk's list of listened-to sockets",
|
||||||
|
oldSock );
|
||||||
|
}
|
||||||
|
if ( newSock != -1 ) {
|
||||||
|
info = (SockInfo*)XP_MALLOC( globals->cGlobals.params->util->mpool,
|
||||||
|
sizeof(*info) );
|
||||||
|
GIOChannel* channel = g_io_channel_unix_new( newSock );
|
||||||
|
g_io_channel_set_close_on_unref( channel, TRUE );
|
||||||
|
guint result = g_io_add_watch( channel,
|
||||||
|
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
|
||||||
|
newConnectionInput,
|
||||||
|
globals );
|
||||||
|
info->channel = channel;
|
||||||
|
info->watch = result;
|
||||||
|
if ( !!*storage ) {
|
||||||
|
XP_FREE( globals->cGlobals.params->util->mpool, *storage );
|
||||||
|
}
|
||||||
|
*storage = info;
|
||||||
|
XP_LOGF( "g_io_add_watch(%d) => %d", newSock, result );
|
||||||
|
}
|
||||||
|
#ifdef XWFEATURE_RELAY
|
||||||
|
globals->cGlobals.socket = newSock;
|
||||||
|
#endif
|
||||||
|
/* A hack for the bluetooth case. */
|
||||||
|
CommsCtxt* comms = globals->cGlobals.game.comms;
|
||||||
|
if ( (comms != NULL) && (comms_getConType(comms) == COMMS_CONN_BT) ) {
|
||||||
|
comms_resendAll( comms, XP_FALSE );
|
||||||
|
}
|
||||||
|
LOG_RETURN_VOID();
|
||||||
|
} /* gtk_socket_changed */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtkGotBuf( void* closure, const XP_U8* buf, XP_U16 len )
|
gtkGotBuf( void* closure, const XP_U8* buf, XP_U16 len )
|
||||||
|
@ -487,7 +527,6 @@ getSelRow( const GtkAppGlobals* apg )
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GtkAppGlobals* g_globals_for_signal;
|
|
||||||
static void
|
static void
|
||||||
handle_sigintterm( int XP_UNUSED(sig) )
|
handle_sigintterm( int XP_UNUSED(sig) )
|
||||||
{
|
{
|
||||||
|
@ -501,6 +540,7 @@ gtkmain( LaunchParams* params )
|
||||||
GtkAppGlobals apg = {0};
|
GtkAppGlobals apg = {0};
|
||||||
|
|
||||||
g_globals_for_signal = &apg;
|
g_globals_for_signal = &apg;
|
||||||
|
|
||||||
struct sigaction act = { .sa_handler = handle_sigintterm };
|
struct sigaction act = { .sa_handler = handle_sigintterm };
|
||||||
sigaction( SIGINT, &act, NULL );
|
sigaction( SIGINT, &act, NULL );
|
||||||
sigaction( SIGTERM, &act, NULL );
|
sigaction( SIGTERM, &act, NULL );
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
|
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 1997 - 2011 by Eric House (xwords@eehouse.org). All rights
|
* Copyright 1997 - 2013 by Eric House (xwords@eehouse.org). All rights
|
||||||
* reserved.
|
* reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -32,6 +32,7 @@
|
||||||
#include "dictnryp.h"
|
#include "dictnryp.h"
|
||||||
#include "linuxmain.h"
|
#include "linuxmain.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
|
#include "linuxutl.h"
|
||||||
|
|
||||||
typedef struct DictStart {
|
typedef struct DictStart {
|
||||||
XP_U32 numNodes;
|
XP_U32 numNodes;
|
||||||
|
@ -92,8 +93,6 @@ getNullTermParam( LinuxDictionaryCtxt* XP_UNUSED_DBG(dctx), const XP_U8** ptr,
|
||||||
XP_U16 len = 1 + XP_STRLEN( (XP_UCHAR*)*ptr );
|
XP_U16 len = 1 + XP_STRLEN( (XP_UCHAR*)*ptr );
|
||||||
XP_UCHAR* result = XP_MALLOC( dctx->super.mpool, len );
|
XP_UCHAR* result = XP_MALLOC( dctx->super.mpool, len );
|
||||||
XP_MEMCPY( result, *ptr, len );
|
XP_MEMCPY( result, *ptr, len );
|
||||||
XP_LOGF( "%s: got param of len %d: \"%s\"", __func__,
|
|
||||||
len, result );
|
|
||||||
*ptr += len;
|
*ptr += len;
|
||||||
*headerLen -= len;
|
*headerLen -= len;
|
||||||
return result;
|
return result;
|
||||||
|
@ -374,16 +373,14 @@ initFromDictFile( LinuxDictionaryCtxt* dctx, const LaunchParams* params,
|
||||||
) {
|
) {
|
||||||
XP_U32 curPos = ptr - dctx->dictBase;
|
XP_U32 curPos = ptr - dctx->dictBase;
|
||||||
gssize dictLength = dctx->dictLength - curPos;
|
gssize dictLength = dctx->dictLength - curPos;
|
||||||
GChecksum* cksum = g_checksum_new( G_CHECKSUM_MD5 );
|
|
||||||
g_checksum_update( cksum, ptr, dictLength );
|
gchar* checksum = g_compute_checksum_for_data( G_CHECKSUM_MD5, ptr, dictLength );
|
||||||
const gchar* sum = g_checksum_get_string( cksum );
|
|
||||||
XP_LOGF( "calculated sum on %d bytes: %s", dictLength, sum );
|
|
||||||
if ( NULL == dctx->super.md5Sum ) {
|
if ( NULL == dctx->super.md5Sum ) {
|
||||||
dctx->super.md5Sum = copyString( dctx->super.mpool, sum );
|
dctx->super.md5Sum = copyString( dctx->super.mpool, checksum );
|
||||||
} else {
|
} else {
|
||||||
XP_ASSERT( 0 == XP_STRCMP( dctx->super.md5Sum, sum ) );
|
XP_ASSERT( 0 == XP_STRCMP( dctx->super.md5Sum, checksum ) );
|
||||||
}
|
}
|
||||||
g_checksum_free( cksum );
|
g_free( checksum );
|
||||||
}
|
}
|
||||||
|
|
||||||
dctx->super.nFaces = numFaces;
|
dctx->super.nFaces = numFaces;
|
||||||
|
|
|
@ -48,8 +48,6 @@
|
||||||
# include <bluetooth/hci_lib.h>
|
# include <bluetooth/hci_lib.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* #include <pthread.h> */
|
|
||||||
|
|
||||||
#include "linuxmain.h"
|
#include "linuxmain.h"
|
||||||
#include "linuxutl.h"
|
#include "linuxutl.h"
|
||||||
#include "linuxbt.h"
|
#include "linuxbt.h"
|
||||||
|
@ -633,6 +631,8 @@ typedef enum {
|
||||||
,CMD_SKIPCONFIRM
|
,CMD_SKIPCONFIRM
|
||||||
,CMD_VERTICALSCORE
|
,CMD_VERTICALSCORE
|
||||||
,CMD_NOPEEK
|
,CMD_NOPEEK
|
||||||
|
,CMD_SPLITPACKETS
|
||||||
|
,CMD_CHAT
|
||||||
#ifdef XWFEATURE_CROSSHAIRS
|
#ifdef XWFEATURE_CROSSHAIRS
|
||||||
,CMD_NOCROSSHAIRS
|
,CMD_NOCROSSHAIRS
|
||||||
#endif
|
#endif
|
||||||
|
@ -680,7 +680,7 @@ typedef struct _CmdInfoRec {
|
||||||
} CmdInfoRec;
|
} CmdInfoRec;
|
||||||
|
|
||||||
static CmdInfoRec CmdInfoRecs[] = {
|
static CmdInfoRec CmdInfoRecs[] = {
|
||||||
{ CMD_HELP, false, "help", "print usage" }
|
{ CMD_HELP, false, "help", "print this message" }
|
||||||
,{ CMD_SKIP_GAMEOVER, false, "skip-final", "skip final scores display" }
|
,{ CMD_SKIP_GAMEOVER, false, "skip-final", "skip final scores display" }
|
||||||
,{ CMD_SHOW_OTHERSCORES, false, "show-other", "show robot/remote scores" }
|
,{ CMD_SHOW_OTHERSCORES, false, "show-other", "show robot/remote scores" }
|
||||||
,{ CMD_HOSTIP, true, "hostip", "remote host ip address (for direct connect)" }
|
,{ CMD_HOSTIP, true, "hostip", "remote host ip address (for direct connect)" }
|
||||||
|
@ -737,6 +737,9 @@ static CmdInfoRec CmdInfoRecs[] = {
|
||||||
,{ CMD_SKIPCONFIRM, false, "skip-confirm", "don't confirm before commit" }
|
,{ CMD_SKIPCONFIRM, false, "skip-confirm", "don't confirm before commit" }
|
||||||
,{ CMD_VERTICALSCORE, false, "vertical", "scoreboard is vertical" }
|
,{ CMD_VERTICALSCORE, false, "vertical", "scoreboard is vertical" }
|
||||||
,{ CMD_NOPEEK, false, "no-peek", "disallow scoreboard tap changing player" }
|
,{ CMD_NOPEEK, false, "no-peek", "disallow scoreboard tap changing player" }
|
||||||
|
,{ CMD_SPLITPACKETS, true, "split-packets", "send tcp packets in "
|
||||||
|
"sections every random MOD <n> seconds to test relay reassembly" }
|
||||||
|
,{ CMD_CHAT, true, "send-chat", "send a chat every <n> seconds" }
|
||||||
#ifdef XWFEATURE_CROSSHAIRS
|
#ifdef XWFEATURE_CROSSHAIRS
|
||||||
,{ CMD_NOCROSSHAIRS, false, "hide-crosshairs",
|
,{ CMD_NOCROSSHAIRS, false, "hide-crosshairs",
|
||||||
"don't show crosshairs on board" }
|
"don't show crosshairs on board" }
|
||||||
|
@ -892,15 +895,14 @@ linux_init_relay_socket( CommonGlobals* cGlobals, const CommsAddrRec* addrRec )
|
||||||
/* make a local copy of the address to send to */
|
/* make a local copy of the address to send to */
|
||||||
sock = socket( AF_INET, SOCK_STREAM, 0 );
|
sock = socket( AF_INET, SOCK_STREAM, 0 );
|
||||||
if ( sock == -1 ) {
|
if ( sock == -1 ) {
|
||||||
XP_DEBUGF( "socket returned -1\n" );
|
XP_DEBUGF( "%s: socket returned -1\n", __func__ );
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
to_sock.sin_port = htons( addrRec->u.ip_relay.port );
|
to_sock.sin_port = htons( addrRec->u.ip_relay.port );
|
||||||
XP_STATUSF( "1: sending to port %d", addrRec->u.ip_relay.port );
|
|
||||||
host = gethostbyname( addrRec->u.ip_relay.hostName );
|
host = gethostbyname( addrRec->u.ip_relay.hostName );
|
||||||
if ( NULL == host ) {
|
if ( NULL == host ) {
|
||||||
XP_WARNF( "%s: gethostbyname(%s) returned -1", __func__,
|
XP_WARNF( "%s: gethostbyname(%s) failed", __func__,
|
||||||
addrRec->u.ip_relay.hostName );
|
addrRec->u.ip_relay.hostName );
|
||||||
sock = -1;
|
sock = -1;
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -928,6 +930,96 @@ linux_init_relay_socket( CommonGlobals* cGlobals, const CommsAddrRec* addrRec )
|
||||||
return sock;
|
return sock;
|
||||||
} /* linux_init_relay_socket */
|
} /* linux_init_relay_socket */
|
||||||
|
|
||||||
|
typedef struct _SendQueueElem {
|
||||||
|
XP_U32 id;
|
||||||
|
size_t len;
|
||||||
|
XP_U8* buf;
|
||||||
|
} SendQueueElem;
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_elem_proc( gpointer data )
|
||||||
|
{
|
||||||
|
SendQueueElem* elem = (SendQueueElem*)data;
|
||||||
|
free( elem->buf );
|
||||||
|
free( elem );
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
send_or_close( CommonGlobals* cGlobals, const XP_U8* buf, size_t len )
|
||||||
|
{
|
||||||
|
size_t nSent = send( cGlobals->socket, buf, len, 0 );
|
||||||
|
bool success = len == nSent;
|
||||||
|
if ( !success ) {
|
||||||
|
close( cGlobals->socket );
|
||||||
|
(*cGlobals->socketChanged)( cGlobals->socketChangedClosure,
|
||||||
|
cGlobals->socket, -1,
|
||||||
|
&cGlobals->storage );
|
||||||
|
cGlobals->socket = -1;
|
||||||
|
|
||||||
|
/* delete all pending packets since the socket's bad */
|
||||||
|
g_slist_free_full( cGlobals->packetQueue, free_elem_proc );
|
||||||
|
cGlobals->packetQueue = NULL;
|
||||||
|
}
|
||||||
|
LOG_RETURNF( "%d", success );
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
sendTimerFired( gpointer data )
|
||||||
|
{
|
||||||
|
CommonGlobals* cGlobals = (CommonGlobals*)data;
|
||||||
|
if ( !!cGlobals->packetQueue ) {
|
||||||
|
guint listLen = g_slist_length( cGlobals->packetQueue );
|
||||||
|
assert( 0 < listLen );
|
||||||
|
SendQueueElem* elem = (SendQueueElem*)cGlobals->packetQueue->data;
|
||||||
|
cGlobals->packetQueue = cGlobals->packetQueue->next;
|
||||||
|
|
||||||
|
XP_LOGF( "%s: sending packet %ld of len %d (%d left)", __func__,
|
||||||
|
elem->id, elem->len, listLen - 1 );
|
||||||
|
bool sent = send_or_close( cGlobals, elem->buf, elem->len );
|
||||||
|
free( elem->buf );
|
||||||
|
free( elem );
|
||||||
|
|
||||||
|
if ( sent && 1 < listLen ) {
|
||||||
|
int when = XP_RANDOM() % (1 + cGlobals->params->splitPackets);
|
||||||
|
(void)g_timeout_add_seconds( when, sendTimerFired, cGlobals );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
send_per_params( const XP_U8* buf, const XP_U16 buflen,
|
||||||
|
CommonGlobals* cGlobals )
|
||||||
|
{
|
||||||
|
bool success;
|
||||||
|
if ( 0 == cGlobals->params->splitPackets ) {
|
||||||
|
success = send_or_close( cGlobals, buf, buflen );
|
||||||
|
} else {
|
||||||
|
for ( int nSent = 0; nSent < buflen; ) {
|
||||||
|
int toSend = buflen / 2;
|
||||||
|
if ( toSend > buflen - nSent ) {
|
||||||
|
toSend = buflen - nSent;
|
||||||
|
}
|
||||||
|
SendQueueElem* elem = malloc( sizeof(*elem) );
|
||||||
|
elem->id = ++cGlobals->nextPacketID;
|
||||||
|
elem->buf = malloc( toSend );
|
||||||
|
XP_MEMCPY( elem->buf, &buf[nSent], toSend );
|
||||||
|
elem->len = toSend;
|
||||||
|
cGlobals->packetQueue =
|
||||||
|
g_slist_append( cGlobals->packetQueue, elem );
|
||||||
|
nSent += toSend;
|
||||||
|
XP_LOGF( "%s: added packet %ld of len %d", __func__,
|
||||||
|
elem->id, elem->len );
|
||||||
|
}
|
||||||
|
int when = XP_RANDOM() % (1 + cGlobals->params->splitPackets);
|
||||||
|
(void)g_timeout_add_seconds( when, sendTimerFired, cGlobals );
|
||||||
|
success = TRUE;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
static XP_S16
|
static XP_S16
|
||||||
linux_tcp_send( CommonGlobals* cGlobals, const XP_U8* buf, XP_U16 buflen,
|
linux_tcp_send( CommonGlobals* cGlobals, const XP_U8* buf, XP_U16 buflen,
|
||||||
const CommsAddrRec* addrRec )
|
const CommsAddrRec* addrRec )
|
||||||
|
@ -950,27 +1042,17 @@ linux_tcp_send( CommonGlobals* cGlobals, const XP_U8* buf, XP_U16 buflen,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( sock != -1 ) {
|
if ( sock != -1 ) {
|
||||||
XP_U16 netLen = htons( buflen );
|
XP_U16 netLen = htons( buflen );
|
||||||
errno = 0;
|
XP_U8 tmp[buflen + sizeof(netLen)];
|
||||||
|
XP_MEMCPY( &tmp[0], &netLen, sizeof(netLen) );
|
||||||
|
XP_MEMCPY( &tmp[sizeof(netLen)], buf, buflen );
|
||||||
|
|
||||||
result = send( sock, &netLen, sizeof(netLen), 0 );
|
if ( send_per_params( tmp, buflen + sizeof(netLen), globals ) ) {
|
||||||
if ( result == sizeof(netLen) ) {
|
result = buflen;
|
||||||
result = send( sock, buf, buflen, 0 );
|
|
||||||
}
|
|
||||||
if ( result <= 0 ) {
|
|
||||||
XP_STATUSF( "closing non-functional socket" );
|
|
||||||
close( sock );
|
|
||||||
(*cGlobals->socketChanged)( cGlobals->socketChangedClosure,
|
|
||||||
sock, -1, &cGlobals->storage );
|
|
||||||
cGlobals->socket = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
XP_STATUSF( "%s: send(sock=%d) returned %d of %d (err=%d)",
|
|
||||||
__func__, sock, result, buflen, errno );
|
|
||||||
} else {
|
|
||||||
XP_LOGF( "%s: socket still -1", __func__ );
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
XP_LOGF( "%s: socket still -1", __func__ );
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} /* linux_tcp_send */
|
} /* linux_tcp_send */
|
||||||
|
@ -1085,13 +1167,15 @@ linux_close_socket( CommonGlobals* cGlobals )
|
||||||
static int
|
static int
|
||||||
blocking_read( int fd, unsigned char* buf, const int len )
|
blocking_read( int fd, unsigned char* buf, const int len )
|
||||||
{
|
{
|
||||||
|
assert( -1 != fd );
|
||||||
int nRead = 0;
|
int nRead = 0;
|
||||||
int tries;
|
int tries;
|
||||||
for ( tries = 5; nRead < len && tries > 0; --tries ) {
|
for ( tries = 5; nRead < len && tries > 0; --tries ) {
|
||||||
// XP_LOGF( "%s: blocking for %d bytes", __func__, len );
|
// XP_LOGF( "%s: blocking for %d bytes", __func__, len );
|
||||||
ssize_t nGot = read( fd, buf + nRead, len - nRead );
|
ssize_t nGot = read( fd, buf + nRead, len - nRead );
|
||||||
if ( nGot == 0 ) {
|
if ( nGot == 0 ) {
|
||||||
XP_LOGF( "%s: read 0; let's try again (%d more times)", __func__, tries );
|
XP_LOGF( "%s: read 0; let's try again (%d more times)", __func__,
|
||||||
|
tries );
|
||||||
usleep( 10000 );
|
usleep( 10000 );
|
||||||
} else if ( nGot < 0 ) {
|
} else if ( nGot < 0 ) {
|
||||||
XP_LOGF( "read => %d (wanted %d), errno=%d (\"%s\")", nRead,
|
XP_LOGF( "read => %d (wanted %d), errno=%d (\"%s\")", nRead,
|
||||||
|
@ -1105,6 +1189,7 @@ blocking_read( int fd, unsigned char* buf, const int len )
|
||||||
nRead = -1;
|
nRead = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XP_LOGF( "%s(fd=%d, sought=%d) => %d", __func__, fd, len, nRead );
|
||||||
return nRead;
|
return nRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1136,14 +1221,36 @@ linux_relay_receive( CommonGlobals* cGlobals, unsigned char* buf, int bufSize )
|
||||||
nRead = -1;
|
nRead = -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ( 0 == XP_RANDOM() % -params->dropNthRcvd ) {
|
nRead = blocking_read( sock, buf, packetSize );
|
||||||
XP_LOGF( "%s: RANDOMLY dropping %dth packet "
|
if ( nRead != packetSize ) {
|
||||||
"per --drop-nth-packet",
|
|
||||||
__func__, params->nPacketsRcvd );
|
|
||||||
nRead = -1;
|
nRead = -1;
|
||||||
|
} else {
|
||||||
|
LaunchParams* params = cGlobals->params;
|
||||||
|
++params->nPacketsRcvd;
|
||||||
|
if ( params->dropNthRcvd == 0 ) {
|
||||||
|
/* do nothing */
|
||||||
|
} else if ( params->dropNthRcvd > 0 ) {
|
||||||
|
if ( params->nPacketsRcvd == params->dropNthRcvd ) {
|
||||||
|
XP_LOGF( "%s: dropping %dth packet per --drop-nth-packet",
|
||||||
|
__func__, params->nPacketsRcvd );
|
||||||
|
nRead = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( 0 == XP_RANDOM() % -params->dropNthRcvd ) {
|
||||||
|
XP_LOGF( "%s: RANDOMLY dropping %dth packet "
|
||||||
|
"per --drop-nth-packet",
|
||||||
|
__func__, params->nPacketsRcvd );
|
||||||
|
nRead = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( -1 == nRead ) {
|
||||||
|
linux_close_socket( cGlobals );
|
||||||
|
comms_transportFailed( cGlobals->game.comms );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
XP_LOGF( "%s=>%d", __func__, nRead );
|
XP_LOGF( "%s=>%d", __func__, nRead );
|
||||||
return nRead;
|
return nRead;
|
||||||
|
@ -1785,7 +1892,6 @@ main( int argc, char** argv )
|
||||||
XP_Bool isServer = XP_FALSE;
|
XP_Bool isServer = XP_FALSE;
|
||||||
char* portNum = NULL;
|
char* portNum = NULL;
|
||||||
char* hostName = "localhost";
|
char* hostName = "localhost";
|
||||||
XP_Bool closeStdin = XP_FALSE;
|
|
||||||
unsigned int seed = defaultRandomSeed();
|
unsigned int seed = defaultRandomSeed();
|
||||||
LaunchParams mainParams;
|
LaunchParams mainParams;
|
||||||
XP_U16 nPlayerDicts = 0;
|
XP_U16 nPlayerDicts = 0;
|
||||||
|
@ -2106,7 +2212,7 @@ main( int argc, char** argv )
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case CMD_CLOSESTDIN:
|
case CMD_CLOSESTDIN:
|
||||||
closeStdin = XP_TRUE;
|
mainParams.closeStdin = XP_TRUE;
|
||||||
break;
|
break;
|
||||||
case CMD_QUITAFTER:
|
case CMD_QUITAFTER:
|
||||||
mainParams.quitAfter = atoi(optarg);
|
mainParams.quitAfter = atoi(optarg);
|
||||||
|
@ -2134,6 +2240,12 @@ main( int argc, char** argv )
|
||||||
case CMD_NOPEEK:
|
case CMD_NOPEEK:
|
||||||
mainParams.allowPeek = XP_FALSE;
|
mainParams.allowPeek = XP_FALSE;
|
||||||
break;
|
break;
|
||||||
|
case CMD_SPLITPACKETS:
|
||||||
|
mainParams.splitPackets = atoi( optarg );
|
||||||
|
break;
|
||||||
|
case CMD_CHAT:
|
||||||
|
mainParams.chatsInterval = atoi(optarg);
|
||||||
|
break;
|
||||||
#ifdef XWFEATURE_CROSSHAIRS
|
#ifdef XWFEATURE_CROSSHAIRS
|
||||||
case CMD_NOCROSSHAIRS:
|
case CMD_NOCROSSHAIRS:
|
||||||
mainParams.hideCrosshairs = XP_TRUE;
|
mainParams.hideCrosshairs = XP_TRUE;
|
||||||
|
@ -2349,7 +2461,7 @@ main( int argc, char** argv )
|
||||||
srandom( seed ); /* init linux random number generator */
|
srandom( seed ); /* init linux random number generator */
|
||||||
XP_LOGF( "seeded srandom with %d", seed );
|
XP_LOGF( "seeded srandom with %d", seed );
|
||||||
|
|
||||||
if ( closeStdin ) {
|
if ( mainParams.closeStdin ) {
|
||||||
fclose( stdin );
|
fclose( stdin );
|
||||||
if ( mainParams.quitAfter < 0 ) {
|
if ( mainParams.quitAfter < 0 ) {
|
||||||
fprintf( stderr, "stdin closed; you'll need some way to quit\n" );
|
fprintf( stderr, "stdin closed; you'll need some way to quit\n" );
|
||||||
|
|
|
@ -98,6 +98,9 @@ typedef struct LaunchParams {
|
||||||
XP_Bool duplicatePackets;
|
XP_Bool duplicatePackets;
|
||||||
XP_Bool skipGameOver;
|
XP_Bool skipGameOver;
|
||||||
XP_Bool useMmap;
|
XP_Bool useMmap;
|
||||||
|
XP_Bool closeStdin;
|
||||||
|
XP_U16 splitPackets;
|
||||||
|
XP_U16 chatsInterval; /* 0 means disabled */
|
||||||
#ifdef XWFEATURE_SEARCHLIMIT
|
#ifdef XWFEATURE_SEARCHLIMIT
|
||||||
XP_Bool allowHintRect;
|
XP_Bool allowHintRect;
|
||||||
#endif
|
#endif
|
||||||
|
@ -194,6 +197,8 @@ struct CommonGlobals {
|
||||||
void* socketChangedClosure;
|
void* socketChangedClosure;
|
||||||
OnSaveFunc onSave;
|
OnSaveFunc onSave;
|
||||||
void* onSaveClosure;
|
void* onSaveClosure;
|
||||||
|
GSList* packetQueue;
|
||||||
|
XP_U32 nextPacketID; /* for debugging */
|
||||||
|
|
||||||
CommsRelayState state;
|
CommsRelayState state;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -u -e
|
set -u -e
|
||||||
|
|
||||||
|
LOGDIR=$(basename $0)_logs
|
||||||
APP_NEW=""
|
APP_NEW=""
|
||||||
|
DO_CLEAN=""
|
||||||
APP_NEW_PARAMS=""
|
APP_NEW_PARAMS=""
|
||||||
NGAMES=""
|
NGAMES=""
|
||||||
UDP_PCT=0
|
UDP_PCT=0
|
||||||
|
@ -25,6 +27,7 @@ SEED=""
|
||||||
BOARD_SIZES_OLD=(15)
|
BOARD_SIZES_OLD=(15)
|
||||||
BOARD_SIZES_NEW=(15)
|
BOARD_SIZES_NEW=(15)
|
||||||
NAMES=(UNUSED Brynn Ariela Kati Eric)
|
NAMES=(UNUSED Brynn Ariela Kati Eric)
|
||||||
|
SEND_CHAT=''
|
||||||
|
|
||||||
declare -A PIDS
|
declare -A PIDS
|
||||||
declare -A APPS
|
declare -A APPS
|
||||||
|
@ -40,11 +43,30 @@ declare -a APPS_OLD
|
||||||
declare -a DICTS
|
declare -a DICTS
|
||||||
declare -A CHECKED_ROOMS
|
declare -A CHECKED_ROOMS
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
APP="$(basename $APP_NEW)"
|
||||||
|
while pidof $APP; do
|
||||||
|
echo "killing existing $APP instances..."
|
||||||
|
killall -9 $APP
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "cleaning everything up...."
|
||||||
|
if [ -d $LOGDIR ]; then
|
||||||
|
mv $LOGDIR /tmp/${LOGDIR}_$$
|
||||||
|
fi
|
||||||
|
if [ -e $(dirname $0)/../../relay/xwrelay.log ]; then
|
||||||
|
mkdir -p /tmp/${LOGDIR}_$$
|
||||||
|
mv $(dirname $0)/../../relay/xwrelay.log /tmp/${LOGDIR}_$$
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "delete from games;" | psql -q -t xwgames
|
||||||
|
}
|
||||||
|
|
||||||
function connName() {
|
function connName() {
|
||||||
LOG=$1
|
LOG=$1
|
||||||
grep 'got_connect_cmd: connName' $LOG | \
|
grep 'got_connect_cmd: connName' $LOG | \
|
||||||
tail -n 1 | \
|
tail -n 1 | \
|
||||||
sed 's,^.*connName: \"\(.*\)\"$,\1,'
|
sed 's,^.*connName: \"\(.*\)\" (reconnect=.)$,\1,'
|
||||||
}
|
}
|
||||||
|
|
||||||
function check_room() {
|
function check_room() {
|
||||||
|
@ -201,6 +223,10 @@ build_cmds() {
|
||||||
PARAMS="$PARAMS --file $FILE"
|
PARAMS="$PARAMS --file $FILE"
|
||||||
fi
|
fi
|
||||||
PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS"
|
PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS"
|
||||||
|
# PARAMS="$PARAMS --split-packets 2"
|
||||||
|
if [ -n $SEND_CHAT ]; then
|
||||||
|
PARAMS="$PARAMS --send-chat $SEND_CHAT"
|
||||||
|
fi
|
||||||
# PARAMS="$PARAMS --savefail-pct 10"
|
# PARAMS="$PARAMS --savefail-pct 10"
|
||||||
[ -n "$SEED" ] && PARAMS="$PARAMS --seed $RANDOM"
|
[ -n "$SEED" ] && PARAMS="$PARAMS --seed $RANDOM"
|
||||||
PARAMS="$PARAMS $PUBLIC"
|
PARAMS="$PARAMS $PUBLIC"
|
||||||
|
@ -453,14 +479,18 @@ run_cmds() {
|
||||||
try_upgrade $KEY
|
try_upgrade $KEY
|
||||||
launch $KEY &
|
launch $KEY &
|
||||||
PID=$!
|
PID=$!
|
||||||
|
renice +1 $PID >/dev/null
|
||||||
PIDS[$KEY]=$PID
|
PIDS[$KEY]=$PID
|
||||||
ROOM_PIDS[$ROOM]=$PID
|
ROOM_PIDS[$ROOM]=$PID
|
||||||
MINEND[$KEY]=$(($NOW + $MINRUN))
|
MINEND[$KEY]=$(($NOW + $MINRUN))
|
||||||
else
|
else
|
||||||
SLEEP=$((${MINEND[$KEY]} - $NOW))
|
PID=${PIDS[$KEY]}
|
||||||
[ $SLEEP -gt 0 ] && sleep $SLEEP
|
if [ -d /proc/$PID ]; then
|
||||||
kill ${PIDS[$KEY]} || true
|
SLEEP=$((${MINEND[$KEY]} - $NOW))
|
||||||
wait ${PIDS[$KEY]}
|
[ $SLEEP -gt 0 ] && sleep $SLEEP
|
||||||
|
kill $PID || true
|
||||||
|
wait $PID
|
||||||
|
fi
|
||||||
PIDS[$KEY]=0
|
PIDS[$KEY]=0
|
||||||
ROOM_PIDS[$ROOM]=0
|
ROOM_PIDS[$ROOM]=0
|
||||||
[ "$DROP_N" -ge 0 ] && increment_drop $KEY
|
[ "$DROP_N" -ge 0 ] && increment_drop $KEY
|
||||||
|
@ -527,6 +557,7 @@ function usage() {
|
||||||
[ $# -gt 0 ] && echo "Error: $1" >&2
|
[ $# -gt 0 ] && echo "Error: $1" >&2
|
||||||
echo "Usage: $(basename $0) \\" >&2
|
echo "Usage: $(basename $0) \\" >&2
|
||||||
echo " [--via-udp <pct>] \\" >&2
|
echo " [--via-udp <pct>] \\" >&2
|
||||||
|
echo " [--clean-start] \\" >&2
|
||||||
echo " [--game-dict <path/to/dict>]* \\" >&2
|
echo " [--game-dict <path/to/dict>]* \\" >&2
|
||||||
echo " [--old-app <path/to/app]* \\" >&2
|
echo " [--old-app <path/to/app]* \\" >&2
|
||||||
echo " [--new-app <path/to/app] \\" >&2
|
echo " [--new-app <path/to/app] \\" >&2
|
||||||
|
@ -540,6 +571,8 @@ function usage() {
|
||||||
echo " [--port <int>] \\" >&2
|
echo " [--port <int>] \\" >&2
|
||||||
echo " [--seed <int>] \\" >&2
|
echo " [--seed <int>] \\" >&2
|
||||||
echo " [--undo-pct <int>] \\" >&2
|
echo " [--undo-pct <int>] \\" >&2
|
||||||
|
echo " [--send-chat <interval-in-seconds> \\" >&2
|
||||||
|
echo " [--resign-ratio <0 <= n <=1000 > \\" >&2
|
||||||
echo " [--help] \\" >&2
|
echo " [--help] \\" >&2
|
||||||
|
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -554,6 +587,8 @@ while [ "$#" -gt 0 ]; do
|
||||||
--via-udp)
|
--via-udp)
|
||||||
UDP_PCT=$(getArg $*)
|
UDP_PCT=$(getArg $*)
|
||||||
shift
|
shift
|
||||||
|
--clean-start)
|
||||||
|
DO_CLEAN=1
|
||||||
;;
|
;;
|
||||||
--num-games)
|
--num-games)
|
||||||
NGAMES=$(getArg $*)
|
NGAMES=$(getArg $*)
|
||||||
|
@ -607,6 +642,14 @@ while [ "$#" -gt 0 ]; do
|
||||||
UNDO_PCT=$(getArg $*)
|
UNDO_PCT=$(getArg $*)
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--send-chat)
|
||||||
|
SEND_CHAT=$(getArg $*)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--resign-ratio)
|
||||||
|
RESIGN_RATIO=$(getArg $*)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--help)
|
--help)
|
||||||
usage
|
usage
|
||||||
;;
|
;;
|
||||||
|
@ -637,7 +680,8 @@ done
|
||||||
[ -n "$SEED" ] && RANDOM=$SEED
|
[ -n "$SEED" ] && RANDOM=$SEED
|
||||||
[ -z "$ONEPER" -a $NROOMS -lt $NGAMES ] && usage "use --one-per if --num-rooms < --num-games"
|
[ -z "$ONEPER" -a $NROOMS -lt $NGAMES ] && usage "use --one-per if --num-rooms < --num-games"
|
||||||
|
|
||||||
LOGDIR=$(basename $0)_logs
|
[ -n "$DO_CLEAN" ] && cleanup
|
||||||
|
|
||||||
RESUME=""
|
RESUME=""
|
||||||
for FILE in $(ls $LOGDIR/*.{xwg,txt} 2>/dev/null); do
|
for FILE in $(ls $LOGDIR/*.{xwg,txt} 2>/dev/null); do
|
||||||
if [ -e $FILE ]; then
|
if [ -e $FILE ]; then
|
||||||
|
|
|
@ -24,7 +24,7 @@ while [ $# -ge 1 ]; do
|
||||||
while read LINE; do
|
while read LINE; do
|
||||||
case "$LINE" in
|
case "$LINE" in
|
||||||
*got_connect_cmd:\ connName* )
|
*got_connect_cmd:\ connName* )
|
||||||
CONNNAME="$(echo $LINE | sed 's,^.*connName: "\(.*\)"$,\1,')"
|
CONNNAME="$(echo $LINE | sed 's,^.*connName: "\(.*\)" .*$,\1,')"
|
||||||
;;
|
;;
|
||||||
*got_connect_cmd:\ set\ hostid* )
|
*got_connect_cmd:\ set\ hostid* )
|
||||||
HOSTID=$(echo $LINE | sed 's,^.*set hostid: \(.\)$,\1,')
|
HOSTID=$(echo $LINE | sed 's,^.*set hostid: \(.\)$,\1,')
|
||||||
|
|
|
@ -45,6 +45,7 @@ OBJ = $(patsubst %.cpp,%.o,$(SRC))
|
||||||
LDFLAGS += -pthread -g $(STATIC)
|
LDFLAGS += -pthread -g $(STATIC)
|
||||||
LDFLAGS += -L$(shell pg_config --libdir)
|
LDFLAGS += -L$(shell pg_config --libdir)
|
||||||
LDFLAGS += $(shell pkg-config --libs glib-2.0)
|
LDFLAGS += $(shell pkg-config --libs glib-2.0)
|
||||||
|
LDFLAGS += -lrt
|
||||||
|
|
||||||
CPPFLAGS += -DSPAWN_SELF -g -Wall
|
CPPFLAGS += -DSPAWN_SELF -g -Wall
|
||||||
CPPFLAGS += -I $(shell pg_config --includedir)
|
CPPFLAGS += -I $(shell pg_config --includedir)
|
||||||
|
|
|
@ -21,8 +21,39 @@
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "addrinfo.h"
|
#include "addrinfo.h"
|
||||||
|
#include "xwrelay_priv.h"
|
||||||
|
#include "tpool.h"
|
||||||
|
|
||||||
|
// static uint32_t s_prevCreated = 0L;
|
||||||
|
|
||||||
|
void
|
||||||
|
AddrInfo::construct( int socket, const AddrUnion* saddr, bool isTCP )
|
||||||
|
{
|
||||||
|
memset( this, 0, sizeof(*this) );
|
||||||
|
|
||||||
|
struct timespec tp;
|
||||||
|
clock_gettime( CLOCK_MONOTONIC, &tp );
|
||||||
|
/* convert to milliseconds */
|
||||||
|
m_created = (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000);
|
||||||
|
logf( XW_LOGINFO, "%s: m_created for socket %d: %lx",
|
||||||
|
__func__, socket, m_created );
|
||||||
|
/* assert( m_created >= s_prevCreated ); */
|
||||||
|
/* s_prevCreated = m_created; */
|
||||||
|
|
||||||
|
m_socket = socket;
|
||||||
|
m_isTCP = isTCP;
|
||||||
|
memcpy( &m_saddr, saddr, sizeof(m_saddr) );
|
||||||
|
m_isValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
AddrInfo::isCurrent() const
|
||||||
|
{
|
||||||
|
return XWThreadPool::GetTPool()->IsCurrent( this );
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
AddrInfo::equals( const AddrInfo& other ) const
|
AddrInfo::equals( const AddrInfo& other ) const
|
||||||
|
@ -31,6 +62,11 @@ AddrInfo::equals( const AddrInfo& other ) const
|
||||||
if ( equal ) {
|
if ( equal ) {
|
||||||
if ( isTCP() ) {
|
if ( isTCP() ) {
|
||||||
equal = m_socket == other.m_socket;
|
equal = m_socket == other.m_socket;
|
||||||
|
if ( equal && created() != other.created() ) {
|
||||||
|
logf( XW_LOGINFO, "%s: rejecting on time mismatch (%lx vs %lx)",
|
||||||
|
__func__, created(), other.created() );
|
||||||
|
equal = false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// assert( m_socket == other.m_socket ); /* both same UDP socket */
|
// assert( m_socket == other.m_socket ); /* both same UDP socket */
|
||||||
/* what does equal mean on udp addresses? Same host, or same host AND game */
|
/* what does equal mean on udp addresses? Same host, or same host AND game */
|
||||||
|
|
|
@ -61,18 +61,13 @@ class AddrInfo {
|
||||||
struct in_addr sin_addr() const { return m_saddr.addr_in.sin_addr; }
|
struct in_addr sin_addr() const { return m_saddr.addr_in.sin_addr; }
|
||||||
const struct sockaddr* sockaddr() const { assert(m_isValid); return &m_saddr.addr; }
|
const struct sockaddr* sockaddr() const { assert(m_isValid); return &m_saddr.addr; }
|
||||||
const AddrUnion* saddr() const { assert(m_isValid); return &m_saddr; }
|
const AddrUnion* saddr() const { assert(m_isValid); return &m_saddr; }
|
||||||
|
uint32_t created() const { return m_created; }
|
||||||
|
bool isCurrent() const;
|
||||||
|
|
||||||
bool equals( const AddrInfo& other ) const;
|
bool equals( const AddrInfo& other ) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void construct( int socket, const AddrUnion* saddr, bool isTCP ) {
|
void construct( int socket, const AddrUnion* saddr, bool isTCP );
|
||||||
memset( this, 0, sizeof(*this) );
|
|
||||||
|
|
||||||
m_socket = socket;
|
|
||||||
m_isTCP = isTCP;
|
|
||||||
memcpy( &m_saddr, saddr, sizeof(m_saddr) );
|
|
||||||
m_isValid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddrInfo& operator=(const AddrInfo&); // Prevent assignment
|
// AddrInfo& operator=(const AddrInfo&); // Prevent assignment
|
||||||
int m_socket;
|
int m_socket;
|
||||||
|
@ -80,6 +75,7 @@ class AddrInfo {
|
||||||
bool m_isValid;
|
bool m_isValid;
|
||||||
ClientToken m_clientToken; /* must be 32 bit */
|
ClientToken m_clientToken; /* must be 32 bit */
|
||||||
AddrUnion m_saddr;
|
AddrUnion m_saddr;
|
||||||
|
uint32_t m_created; /* microseconds since boot, from clock_gettime() */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
|
/* -*- -*- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2005-2011 by Eric House (xwords@eehouse.org). All rights
|
* Copyright 2005-2011 by Eric House (xwords@eehouse.org). All rights
|
||||||
|
@ -34,6 +34,22 @@ CidInfo::GetAddrs( void )
|
||||||
m_addrs : m_cref->GetAddrs();
|
m_addrs : m_cref->GetAddrs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CidInfo::SetOwner( pthread_t owner )
|
||||||
|
{
|
||||||
|
if ( 0 == owner ) {
|
||||||
|
if ( 0 == --m_ownerCount ) {
|
||||||
|
m_owner = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
++m_ownerCount;
|
||||||
|
assert( 0 == m_owner || owner == m_owner );
|
||||||
|
m_owner = owner;
|
||||||
|
}
|
||||||
|
assert( 0 <= m_ownerCount );
|
||||||
|
logf( XW_LOGINFO, "%s(owner=%d); m_ownerCount=%d", __func__, owner, m_ownerCount );
|
||||||
|
}
|
||||||
|
|
||||||
CidLock* CidLock::s_instance = NULL;
|
CidLock* CidLock::s_instance = NULL;
|
||||||
|
|
||||||
CidLock::CidLock() : m_nextCID(0)
|
CidLock::CidLock() : m_nextCID(0)
|
||||||
|
@ -56,7 +72,7 @@ CidLock::print_claimed( const char* caller )
|
||||||
string str;
|
string str;
|
||||||
string_printf( str, "after %s: ", caller );
|
string_printf( str, "after %s: ", caller );
|
||||||
// Assume we have the mutex!!!!
|
// Assume we have the mutex!!!!
|
||||||
map< CookieID, CidInfo*>::iterator iter;
|
map< CookieID, CidInfo*>::const_iterator iter;
|
||||||
for ( iter = m_infos.begin(); iter != m_infos.end(); ++iter ) {
|
for ( iter = m_infos.begin(); iter != m_infos.end(); ++iter ) {
|
||||||
CidInfo* info = iter->second;
|
CidInfo* info = iter->second;
|
||||||
if ( 0 == info->GetOwner() ) {
|
if ( 0 == info->GetOwner() ) {
|
||||||
|
@ -65,7 +81,7 @@ CidLock::print_claimed( const char* caller )
|
||||||
string_printf( str, "%d,", info->GetCid() );
|
string_printf( str, "%d,", info->GetCid() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
string_printf( str, "%d,", " (plus %d unclaimed.)", unclaimed );
|
string_printf( str, " (plus %d unclaimed.)", unclaimed );
|
||||||
logf( XW_LOGINFO, "%s: claimed: %s", __func__, str.c_str() );
|
logf( XW_LOGINFO, "%s: claimed: %s", __func__, str.c_str() );
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -73,12 +89,14 @@ CidLock::print_claimed( const char* caller )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
CidInfo*
|
CidInfo*
|
||||||
CidLock::Claim( CookieID cid )
|
CidLock::Claim( const CookieID origCid )
|
||||||
{
|
{
|
||||||
|
CookieID cid = origCid;
|
||||||
#ifdef CIDLOCK_DEBUG
|
#ifdef CIDLOCK_DEBUG
|
||||||
logf( XW_LOGINFO, "%s(%d)", __func__, cid );
|
logf( XW_LOGINFO, "%s(%d)", __func__, origCid );
|
||||||
#endif
|
#endif
|
||||||
CidInfo* info = NULL;
|
CidInfo* info = NULL;
|
||||||
|
pthread_t self = pthread_self();
|
||||||
for ( ; ; ) {
|
for ( ; ; ) {
|
||||||
MutexLock ml( &m_infos_mutex );
|
MutexLock ml( &m_infos_mutex );
|
||||||
|
|
||||||
|
@ -92,13 +110,14 @@ CidLock::Claim( CookieID cid )
|
||||||
info = new CidInfo( cid );
|
info = new CidInfo( cid );
|
||||||
m_infos.insert( pair<CookieID, CidInfo*>( cid, info ) );
|
m_infos.insert( pair<CookieID, CidInfo*>( cid, info ) );
|
||||||
} else {
|
} else {
|
||||||
if ( 0 == iter->second->GetOwner() ) {
|
pthread_t owner = iter->second->GetOwner();
|
||||||
|
if ( 0 == owner || self == owner ) {
|
||||||
info = iter->second;
|
info = iter->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( NULL != info ) { // we're done
|
if ( NULL != info ) { // we're done
|
||||||
info->SetOwner( pthread_self() );
|
info->SetOwner( self );
|
||||||
PRINT_CLAIMED();
|
PRINT_CLAIMED();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +128,7 @@ CidLock::Claim( CookieID cid )
|
||||||
pthread_cond_wait( &m_infos_condvar, &m_infos_mutex );
|
pthread_cond_wait( &m_infos_condvar, &m_infos_mutex );
|
||||||
}
|
}
|
||||||
#ifdef CIDLOCK_DEBUG
|
#ifdef CIDLOCK_DEBUG
|
||||||
logf( XW_LOGINFO, "%s(%d): DONE", __func__, cid );
|
logf( XW_LOGINFO, "%s(%d): DONE", __func__, origCid );
|
||||||
#endif
|
#endif
|
||||||
return info;
|
return info;
|
||||||
} /* CidLock::Claim */
|
} /* CidLock::Claim */
|
||||||
|
@ -119,23 +138,25 @@ CidLock::ClaimSocket( const AddrInfo* addr )
|
||||||
{
|
{
|
||||||
CidInfo* info = NULL;
|
CidInfo* info = NULL;
|
||||||
#ifdef CIDLOCK_DEBUG
|
#ifdef CIDLOCK_DEBUG
|
||||||
logf( XW_LOGINFO, "%s(sock=%d)", __func__, sock );
|
logf( XW_LOGINFO, "%s(sock=%d)", __func__, addr->socket() );
|
||||||
#endif
|
#endif
|
||||||
for ( ; ; ) {
|
for ( ; ; ) {
|
||||||
MutexLock ml( &m_infos_mutex );
|
MutexLock ml( &m_infos_mutex );
|
||||||
|
|
||||||
map<CookieID, CidInfo*>::iterator iter;
|
map<CookieID, CidInfo*>::const_iterator iter;
|
||||||
for ( iter = m_infos.begin(); NULL == info && iter != m_infos.end(); ++iter ) {
|
for ( iter = m_infos.begin(); NULL == info && iter != m_infos.end();
|
||||||
|
++iter ) {
|
||||||
const vector<AddrInfo>& addrs = iter->second->GetAddrs();
|
const vector<AddrInfo>& addrs = iter->second->GetAddrs();
|
||||||
vector<AddrInfo>::const_iterator iter2;
|
vector<AddrInfo>::const_iterator iter2;
|
||||||
for ( iter2 = addrs.begin(); iter2 != addrs.end(); ++iter2 ) {
|
for ( iter2 = addrs.begin(); iter2 != addrs.end(); ++iter2 ) {
|
||||||
if ( iter2->equals(*addr) ) {
|
if ( iter2->equals(*addr) ) {
|
||||||
|
assert( !info ); // I hit this -- twice!!!!
|
||||||
if ( 0 == iter->second->GetOwner() ) {
|
if ( 0 == iter->second->GetOwner() ) {
|
||||||
info = iter->second;
|
info = iter->second;
|
||||||
info->SetOwner( pthread_self() );
|
info->SetOwner( pthread_self() );
|
||||||
PRINT_CLAIMED();
|
PRINT_CLAIMED();
|
||||||
}
|
}
|
||||||
break;
|
// break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +166,7 @@ CidLock::ClaimSocket( const AddrInfo* addr )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#ifdef CIDLOCK_DEBUG
|
#ifdef CIDLOCK_DEBUG
|
||||||
logf( XW_LOGINFO, "%s(sock=%d): waiting....", __func__, sock );
|
logf( XW_LOGINFO, "%s(sock=%d): waiting....", __func__, addr->socket() );
|
||||||
#endif
|
#endif
|
||||||
pthread_cond_wait( &m_infos_condvar, &m_infos_mutex );
|
pthread_cond_wait( &m_infos_condvar, &m_infos_mutex );
|
||||||
}
|
}
|
||||||
|
@ -171,9 +192,11 @@ CidLock::Relinquish( CidInfo* claim, bool drop )
|
||||||
assert( claim->GetOwner() == pthread_self() );
|
assert( claim->GetOwner() == pthread_self() );
|
||||||
if ( drop ) {
|
if ( drop ) {
|
||||||
#ifdef CIDLOCK_DEBUG
|
#ifdef CIDLOCK_DEBUG
|
||||||
logf( XW_LOGINFO, "%s: deleting %p", __func__, iter->second );
|
logf( XW_LOGINFO, "%s: deleting %p (cid=%d)",
|
||||||
|
__func__, claim, claim->GetCid() );
|
||||||
#endif
|
#endif
|
||||||
m_infos.erase( iter );
|
m_infos.erase( iter );
|
||||||
|
claim->SetOwner( 0 );
|
||||||
delete claim;
|
delete claim;
|
||||||
} else {
|
} else {
|
||||||
CookieRef* ref = claim->GetRef();
|
CookieRef* ref = claim->GetRef();
|
||||||
|
|
|
@ -34,7 +34,10 @@ class CidInfo {
|
||||||
CidInfo( CookieID cid )
|
CidInfo( CookieID cid )
|
||||||
:m_cid(cid),
|
:m_cid(cid),
|
||||||
m_cref(NULL),
|
m_cref(NULL),
|
||||||
m_owner(0) {}
|
m_owner(0),
|
||||||
|
m_ownerCount(0) {}
|
||||||
|
|
||||||
|
~CidInfo() { assert( 0 == m_ownerCount ); }
|
||||||
|
|
||||||
CookieID GetCid( void ) { return m_cid; }
|
CookieID GetCid( void ) { return m_cid; }
|
||||||
CookieRef* GetRef( void ) { return m_cref; }
|
CookieRef* GetRef( void ) { return m_cref; }
|
||||||
|
@ -43,12 +46,13 @@ class CidInfo {
|
||||||
void SetAddrs( vector<AddrInfo> addrs ) { m_addrs = addrs; };
|
void SetAddrs( vector<AddrInfo> addrs ) { m_addrs = addrs; };
|
||||||
|
|
||||||
void SetRef( CookieRef* cref ) { m_cref = cref; }
|
void SetRef( CookieRef* cref ) { m_cref = cref; }
|
||||||
void SetOwner( pthread_t owner ) { m_owner = owner; }
|
void SetOwner( pthread_t owner );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CookieID m_cid;
|
CookieID m_cid;
|
||||||
CookieRef* m_cref;
|
CookieRef* m_cref;
|
||||||
pthread_t m_owner;
|
pthread_t m_owner;
|
||||||
|
int m_ownerCount;
|
||||||
vector<AddrInfo> m_addrs;
|
vector<AddrInfo> m_addrs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ CookieRef::ReInit( const char* cookie, const char* connName, CookieID cid,
|
||||||
} else {
|
} else {
|
||||||
m_delayMicros = 0;
|
m_delayMicros = 0;
|
||||||
}
|
}
|
||||||
RelayConfigs::GetConfigs()->GetValueFor( "HEARTBEAT", &m_heatbeat );
|
RelayConfigs::GetConfigs()->GetValueFor( "HEARTBEAT", &m_heartbeat );
|
||||||
logf( XW_LOGINFO, "initing cref for cookie %s, connName %s",
|
logf( XW_LOGINFO, "initing cref for cookie %s, connName %s",
|
||||||
m_cookie.c_str(), m_connName.c_str() );
|
m_cookie.c_str(), m_connName.c_str() );
|
||||||
|
|
||||||
|
@ -422,8 +422,8 @@ CookieRef::removeSocket( const AddrInfo* addr )
|
||||||
if ( iter->m_addr.equals( *addr ) ) {
|
if ( iter->m_addr.equals( *addr ) ) {
|
||||||
if ( iter->m_ackPending ) {
|
if ( iter->m_ackPending ) {
|
||||||
logf( XW_LOGINFO,
|
logf( XW_LOGINFO,
|
||||||
"Never got ack; removing hid %d from DB",
|
"%s: Never got ack; removing hid %d from DB",
|
||||||
iter->m_hostID );
|
__func__, iter->m_hostID );
|
||||||
DBMgr::Get()->RmDeviceByHid( ConnName(),
|
DBMgr::Get()->RmDeviceByHid( ConnName(),
|
||||||
iter->m_hostID );
|
iter->m_hostID );
|
||||||
m_nPlayersHere -= iter->m_nPlayersH;
|
m_nPlayersHere -= iter->m_nPlayersH;
|
||||||
|
@ -857,22 +857,19 @@ void
|
||||||
CookieRef::send_stored_messages( HostID dest, const AddrInfo* addr )
|
CookieRef::send_stored_messages( HostID dest, const AddrInfo* addr )
|
||||||
{
|
{
|
||||||
logf( XW_LOGVERBOSE0, "%s(dest=%d)", __func__, dest );
|
logf( XW_LOGVERBOSE0, "%s(dest=%d)", __func__, dest );
|
||||||
|
|
||||||
assert( dest > 0 && dest <= 4 );
|
assert( dest > 0 && dest <= 4 );
|
||||||
assert( -1 != addr->socket() );
|
|
||||||
|
|
||||||
for ( ; ; ) {
|
DBMgr* dbmgr = DBMgr::Get();
|
||||||
unsigned char buf[MAX_MSG_LEN];
|
const char* cname = ConnName();
|
||||||
size_t buflen = sizeof(buf);
|
while ( addr->isCurrent() ) {
|
||||||
int msgID;
|
unsigned char buf[MAX_MSG_LEN];
|
||||||
if ( !DBMgr::Get()->GetStoredMessage( ConnName(), dest,
|
size_t buflen = sizeof(buf);
|
||||||
buf, &buflen, &msgID ) ) {
|
int msgID;
|
||||||
break;
|
if ( !dbmgr->GetStoredMessage( cname, dest, buf, &buflen, &msgID )
|
||||||
}
|
|| ! send_with_length( addr, dest, buf, buflen, true ) ) {
|
||||||
if ( ! send_with_length( addr, dest, buf, buflen, true ) ) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
dbmgr->RemoveStoredMessages( &msgID, 1 );
|
||||||
DBMgr::Get()->RemoveStoredMessages( &msgID, 1 );
|
|
||||||
}
|
}
|
||||||
} /* send_stored_messages */
|
} /* send_stored_messages */
|
||||||
|
|
||||||
|
@ -936,6 +933,8 @@ CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp,
|
||||||
{
|
{
|
||||||
RWWriteLock rwl( &m_socketsRWLock );
|
RWWriteLock rwl( &m_socketsRWLock );
|
||||||
HostRec hr( hostid, &evt->addr, nPlayersH, seed, !reconn );
|
HostRec hr( hostid, &evt->addr, nPlayersH, seed, !reconn );
|
||||||
|
logf( XW_LOGINFO, "%s: adding socket rec with ts %lx", __func__,
|
||||||
|
evt->addr.created() );
|
||||||
m_sockets.push_back( hr );
|
m_sockets.push_back( hr );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1291,7 +1290,7 @@ CookieRef::sendAllHere( bool initial )
|
||||||
message for it. Would be better if could look up rather than run
|
message for it. Would be better if could look up rather than run
|
||||||
through the vector each time. */
|
through the vector each time. */
|
||||||
HostID dest;
|
HostID dest;
|
||||||
for ( dest = 1; dest <= m_nPlayersHere; ++dest ) {
|
for ( dest = 1; dest <= m_nPlayersSought; ++dest ) {
|
||||||
bool sent = false;
|
bool sent = false;
|
||||||
*idLoc = dest; /* write in this target's hostId */
|
*idLoc = dest; /* write in this target's hostId */
|
||||||
|
|
||||||
|
@ -1461,7 +1460,7 @@ CookieRef::logf( XW_LogLevel level, const char* format, ... )
|
||||||
char buf[256];
|
char buf[256];
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
len = snprintf( buf, sizeof(buf), "cid:%d ", m_cid );
|
len = snprintf( buf, sizeof(buf), "cid:%d(%s) ", m_cid, m_connName.c_str() );
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start( ap, format );
|
va_start( ap, format );
|
||||||
|
|
|
@ -104,7 +104,7 @@ class CookieRef {
|
||||||
const char* Cookie() const { return m_cookie.c_str(); }
|
const char* Cookie() const { return m_cookie.c_str(); }
|
||||||
const char* ConnName() { return m_connName.c_str(); }
|
const char* ConnName() { return m_connName.c_str(); }
|
||||||
|
|
||||||
int GetHeartbeat() { return m_heatbeat; }
|
int GetHeartbeat() { return m_heartbeat; }
|
||||||
const AddrInfo* SocketForHost( HostID dest );
|
const AddrInfo* SocketForHost( HostID dest );
|
||||||
HostID HostForSocket( const AddrInfo* addr );
|
HostID HostForSocket( const AddrInfo* addr );
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ class CookieRef {
|
||||||
pthread_rwlock_t m_socketsRWLock;
|
pthread_rwlock_t m_socketsRWLock;
|
||||||
vector<HostRec> m_sockets;
|
vector<HostRec> m_sockets;
|
||||||
|
|
||||||
int m_heatbeat; /* might change per carrier or something. */
|
int m_heartbeat; /* might change per carrier or something. */
|
||||||
string m_cookie; /* cookie used for initial connections */
|
string m_cookie; /* cookie used for initial connections */
|
||||||
string m_connName; /* globally unique name */
|
string m_connName; /* globally unique name */
|
||||||
CookieID m_cid; /* Unique among current games on this server */
|
CookieID m_cid; /* Unique among current games on this server */
|
||||||
|
|
|
@ -216,9 +216,8 @@ CRefMgr::getFromFreeList( void )
|
||||||
|
|
||||||
/* connect case */
|
/* connect case */
|
||||||
CidInfo*
|
CidInfo*
|
||||||
CRefMgr::getMakeCookieRef( const char* cookie, HostID hid,
|
CRefMgr::getMakeCookieRef( const char* cookie, int nPlayersH, int nPlayersT,
|
||||||
int nPlayersH, int nPlayersT, int langCode,
|
int langCode, int seed, bool wantsPublic,
|
||||||
int seed, bool wantsPublic,
|
|
||||||
bool makePublic, bool* seenSeed )
|
bool makePublic, bool* seenSeed )
|
||||||
{
|
{
|
||||||
CidInfo* cinfo;
|
CidInfo* cinfo;
|
||||||
|
@ -291,18 +290,21 @@ CRefMgr::getMakeCookieRef( const char* connName, const char* cookie,
|
||||||
int langCode, bool isPublic, bool* isDead )
|
int langCode, bool isPublic, bool* isDead )
|
||||||
{
|
{
|
||||||
CookieRef* cref = NULL;
|
CookieRef* cref = NULL;
|
||||||
CidInfo* cinfo;
|
CidInfo* cinfo = NULL;
|
||||||
|
|
||||||
for ( ; ; ) { /* for: see comment above */
|
for ( ; ; ) { /* for: see comment above */
|
||||||
/* fetch these from DB */
|
/* fetch these from DB */
|
||||||
char curCookie[MAX_INVITE_LEN+1];
|
char curCookie[MAX_INVITE_LEN+1];
|
||||||
int curLangCode;
|
int curLangCode;
|
||||||
int nPlayersT = 0;
|
int nAlreadyHere = nPlayersH;
|
||||||
int nAlreadyHere = 0;
|
|
||||||
|
CookieID cid;
|
||||||
|
if ( !m_db->FindGameFor( connName, curCookie, sizeof(curCookie),
|
||||||
|
seed, hid, nPlayersH, nPlayersS,
|
||||||
|
&curLangCode, isDead, &cid ) ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
CookieID cid = m_db->FindGame( connName, curCookie, sizeof(curCookie),
|
|
||||||
&curLangCode, &nPlayersT, &nAlreadyHere,
|
|
||||||
isDead );
|
|
||||||
if ( 0 != cid ) { /* already open */
|
if ( 0 != cid ) { /* already open */
|
||||||
cinfo = m_cidlock->Claim( cid );
|
cinfo = m_cidlock->Claim( cid );
|
||||||
if ( NULL == cinfo->GetRef() ) {
|
if ( NULL == cinfo->GetRef() ) {
|
||||||
|
@ -315,25 +317,19 @@ CRefMgr::getMakeCookieRef( const char* connName, const char* cookie,
|
||||||
cinfo = m_cidlock->Claim();
|
cinfo = m_cidlock->Claim();
|
||||||
cid = cinfo->GetCid();
|
cid = cinfo->GetCid();
|
||||||
|
|
||||||
if ( nPlayersT == 0 ) { /* wasn't in the DB */
|
if ( !m_db->AddCID( connName, cid ) ) {
|
||||||
m_db->AddNew( cookie, connName, cid, langCode, nPlayersS, isPublic );
|
m_cidlock->Relinquish( cinfo, true );
|
||||||
curLangCode = langCode;
|
continue;
|
||||||
nPlayersT = nPlayersS;
|
|
||||||
} else {
|
|
||||||
if ( !m_db->AddCID( connName, cid ) ) {
|
|
||||||
m_cidlock->Relinquish( cinfo, true );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
cookie = curCookie;
|
|
||||||
}
|
}
|
||||||
|
cookie = curCookie;
|
||||||
|
|
||||||
cref = AddNew( cookie, connName, cid, curLangCode, nPlayersT,
|
cref = AddNew( cookie, connName, cid, curLangCode, nPlayersS,
|
||||||
nAlreadyHere );
|
nAlreadyHere );
|
||||||
cinfo->SetRef( cref );
|
cinfo->SetRef( cref );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} /* for */
|
} /* for */
|
||||||
assert( cinfo->GetRef() );
|
assert( NULL == cinfo || cinfo->GetRef() );
|
||||||
return cinfo;
|
return cinfo;
|
||||||
} /* getMakeCookieRef */
|
} /* getMakeCookieRef */
|
||||||
|
|
||||||
|
@ -402,12 +398,12 @@ CRefMgr::PrintSocketInfo( int socket, string& out )
|
||||||
}
|
}
|
||||||
|
|
||||||
CidInfo*
|
CidInfo*
|
||||||
CRefMgr::getCookieRef( CookieID cid, bool failOk )
|
CRefMgr::getCookieRef( CookieID cid, bool failOk /* = false */ )
|
||||||
{
|
{
|
||||||
CidInfo* cinfo = NULL;
|
CidInfo* cinfo = NULL;
|
||||||
for ( int count = 0; ; ++count ) {
|
for ( int count = 0; ; ++count ) {
|
||||||
cinfo = m_cidlock->Claim( cid );
|
cinfo = m_cidlock->Claim( cid );
|
||||||
if ( NULL != cinfo->GetRef() ) {
|
if ( NULL != cinfo->GetRef() ) { /* What's it mean to get a cinfo back but have it be empty??? */
|
||||||
break;
|
break;
|
||||||
} else if ( failOk || count > 20 ) {
|
} else if ( failOk || count > 20 ) {
|
||||||
break;
|
break;
|
||||||
|
@ -474,9 +470,10 @@ CRefMgr::AddNew( const char* cookie, const char* connName, CookieID cid,
|
||||||
if ( m_cookieMap.size() == 1 ) {
|
if ( m_cookieMap.size() == 1 ) {
|
||||||
RelayConfigs* cfg = RelayConfigs::GetConfigs();
|
RelayConfigs* cfg = RelayConfigs::GetConfigs();
|
||||||
int heartbeat;
|
int heartbeat;
|
||||||
cfg->GetValueFor( "HEARTBEAT", &heartbeat );
|
if ( cfg->GetValueFor( "HEARTBEAT", &heartbeat ) ) {
|
||||||
TimerMgr::GetTimerMgr()->SetTimer( heartbeat, heartbeatProc, this,
|
TimerMgr::GetTimerMgr()->SetTimer( heartbeat, heartbeatProc, this,
|
||||||
heartbeat );
|
heartbeat );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -609,8 +606,8 @@ SafeCref::SafeCref( const char* cookie, const AddrInfo* addr, int clientVers,
|
||||||
{
|
{
|
||||||
CidInfo* cinfo;
|
CidInfo* cinfo;
|
||||||
|
|
||||||
cinfo = m_mgr->getMakeCookieRef( cookie, 0, nPlayersH, nPlayersS, langCode,
|
cinfo = m_mgr->getMakeCookieRef( cookie, nPlayersH, nPlayersS,
|
||||||
gameSeed, wantsPublic, makePublic,
|
langCode, gameSeed, wantsPublic, makePublic,
|
||||||
&m_seenSeed );
|
&m_seenSeed );
|
||||||
if ( cinfo != NULL ) {
|
if ( cinfo != NULL ) {
|
||||||
CookieRef* cref = cinfo->GetRef();
|
CookieRef* cref = cinfo->GetRef();
|
||||||
|
@ -620,16 +617,23 @@ SafeCref::SafeCref( const char* cookie, const AddrInfo* addr, int clientVers,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* REconnect case */
|
/* Reconnect case
|
||||||
|
*
|
||||||
|
* Device thinks it's connected, but we may disagree, e.g. if it sent an ACK
|
||||||
|
* we didn't receive in time. So we may actually wind up creating a new row,
|
||||||
|
* with a new connname, in the games DB in response to this!
|
||||||
|
*
|
||||||
|
*/
|
||||||
SafeCref::SafeCref( const char* connName, const char* cookie, HostID hid,
|
SafeCref::SafeCref( const char* connName, const char* cookie, HostID hid,
|
||||||
const AddrInfo* addr, int clientVers, DevID* devID, int nPlayersH,
|
const AddrInfo* addr, int clientVers, DevID* devID,
|
||||||
int nPlayersS, unsigned short gameSeed, int langCode,
|
int nPlayersH, int nPlayersS, unsigned short gameSeed,
|
||||||
bool wantsPublic, bool makePublic )
|
int langCode, bool wantsPublic, bool makePublic )
|
||||||
: m_cinfo( NULL )
|
: m_cinfo( NULL )
|
||||||
, m_mgr( CRefMgr::Get() )
|
, m_mgr( CRefMgr::Get() )
|
||||||
, m_addr( *addr )
|
, m_addr( *addr )
|
||||||
, m_clientVersion( clientVers )
|
, m_clientVersion( clientVers )
|
||||||
, m_devID( devID )
|
, m_devID( devID )
|
||||||
|
, m_hid( hid )
|
||||||
, m_isValid( false )
|
, m_isValid( false )
|
||||||
{
|
{
|
||||||
CidInfo* cinfo;
|
CidInfo* cinfo;
|
||||||
|
@ -639,6 +643,15 @@ SafeCref::SafeCref( const char* connName, const char* cookie, HostID hid,
|
||||||
cinfo = m_mgr->getMakeCookieRef( connName, cookie, hid, nPlayersH,
|
cinfo = m_mgr->getMakeCookieRef( connName, cookie, hid, nPlayersH,
|
||||||
nPlayersS, gameSeed, langCode,
|
nPlayersS, gameSeed, langCode,
|
||||||
wantsPublic || makePublic, &isDead );
|
wantsPublic || makePublic, &isDead );
|
||||||
|
|
||||||
|
/* If the reconnect doesn't check out, treat it as a connect */
|
||||||
|
if ( NULL == cinfo ) {
|
||||||
|
logf( XW_LOGINFO, "%s: taking a second crack", __func__ );
|
||||||
|
m_hid = HOST_ID_NONE;
|
||||||
|
cinfo = m_mgr->getMakeCookieRef( cookie, nPlayersH, nPlayersS,
|
||||||
|
langCode, gameSeed,
|
||||||
|
wantsPublic, makePublic, &m_seenSeed );
|
||||||
|
}
|
||||||
if ( cinfo != NULL ) {
|
if ( cinfo != NULL ) {
|
||||||
assert( cinfo->GetCid() == cinfo->GetRef()->GetCid() );
|
assert( cinfo->GetCid() == cinfo->GetRef()->GetCid() );
|
||||||
m_locked = cinfo->GetRef()->Lock();
|
m_locked = cinfo->GetRef()->Lock();
|
||||||
|
@ -665,7 +678,7 @@ SafeCref::SafeCref( const char* const connName )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SafeCref::SafeCref( CookieID cid, bool failOk )
|
SafeCref::SafeCref( CookieID cid, bool failOk /* = false */ )
|
||||||
: m_cinfo( NULL )
|
: m_cinfo( NULL )
|
||||||
, m_mgr( CRefMgr::Get() )
|
, m_mgr( CRefMgr::Get() )
|
||||||
, m_isValid( false )
|
, m_isValid( false )
|
||||||
|
|
|
@ -117,7 +117,7 @@ class CRefMgr {
|
||||||
CookieRef* getFromFreeList( void );
|
CookieRef* getFromFreeList( void );
|
||||||
|
|
||||||
/* connect case */
|
/* connect case */
|
||||||
CidInfo* getMakeCookieRef( const char* cookie, HostID hid, int nPlayersH,
|
CidInfo* getMakeCookieRef( const char* cookie, int nPlayersH,
|
||||||
int nPlayersS, int langCode, int seed,
|
int nPlayersS, int langCode, int seed,
|
||||||
bool wantsPublic, bool makePublic,
|
bool wantsPublic, bool makePublic,
|
||||||
bool* seenSeed );
|
bool* seenSeed );
|
||||||
|
@ -187,14 +187,15 @@ class SafeCref {
|
||||||
|
|
||||||
bool Forward( HostID src, const AddrInfo* addr, HostID dest,
|
bool Forward( HostID src, const AddrInfo* addr, HostID dest,
|
||||||
const unsigned char* buf, int buflen ) {
|
const unsigned char* buf, int buflen ) {
|
||||||
if ( IsValid() ) {
|
bool success = IsValid();
|
||||||
|
if ( success ) {
|
||||||
CookieRef* cref = m_cinfo->GetRef();
|
CookieRef* cref = m_cinfo->GetRef();
|
||||||
assert( 0 != cref->GetCid() );
|
assert( 0 != cref->GetCid() );
|
||||||
cref->_Forward( src, addr, dest, buf, buflen );
|
cref->_Forward( src, addr, dest, buf, buflen );
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
logf( XW_LOGINFO, "%s: unable to forward", __func__ );
|
||||||
}
|
}
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PutMsg( HostID srcID, const AddrInfo* addr, HostID destID,
|
void PutMsg( HostID srcID, const AddrInfo* addr, HostID destID,
|
||||||
|
@ -217,8 +218,7 @@ class SafeCref {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool Reconnect( HostID srcID, int nPlayersH, int nPlayersS,
|
bool Reconnect( int nPlayersH, int nPlayersS, int seed, XWREASON* errp ) {
|
||||||
int seed, XWREASON* errp ) {
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
*errp = XWRELAY_ERROR_NONE;
|
*errp = XWRELAY_ERROR_NONE;
|
||||||
if ( IsValid() ) {
|
if ( IsValid() ) {
|
||||||
|
@ -228,7 +228,7 @@ class SafeCref {
|
||||||
*errp = XWRELAY_ERROR_DEADGAME;
|
*errp = XWRELAY_ERROR_DEADGAME;
|
||||||
} else {
|
} else {
|
||||||
success = cref->_Reconnect( m_clientVersion, m_devID,
|
success = cref->_Reconnect( m_clientVersion, m_devID,
|
||||||
srcID, nPlayersH, nPlayersS, seed,
|
m_hid, nPlayersH, nPlayersS, seed,
|
||||||
&m_addr, m_dead );
|
&m_addr, m_dead );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,14 +252,14 @@ class SafeCref {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HandleAck(HostID hostID ) {
|
bool HandleAck(HostID hostID ) {
|
||||||
if ( IsValid() ) {
|
bool handled = IsValid();
|
||||||
|
if ( handled ) {
|
||||||
CookieRef* cref = m_cinfo->GetRef();
|
CookieRef* cref = m_cinfo->GetRef();
|
||||||
assert( 0 != cref->GetCid() );
|
assert( 0 != cref->GetCid() );
|
||||||
cref->_HandleAck( hostID );
|
cref->_HandleAck( hostID );
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
logf( XW_LOGINFO, "%s => %d", __func__, handled );
|
||||||
|
return handled;
|
||||||
}
|
}
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
if ( IsValid() ) {
|
if ( IsValid() ) {
|
||||||
|
@ -391,6 +391,7 @@ class SafeCref {
|
||||||
AddrInfo m_addr;
|
AddrInfo m_addr;
|
||||||
int m_clientVersion;
|
int m_clientVersion;
|
||||||
DevID* m_devID;
|
DevID* m_devID;
|
||||||
|
HostID m_hid;
|
||||||
bool m_isValid;
|
bool m_isValid;
|
||||||
bool m_locked;
|
bool m_locked;
|
||||||
bool m_dead;
|
bool m_dead;
|
||||||
|
|
|
@ -65,6 +65,7 @@ DBMgr::DBMgr()
|
||||||
int tmp;
|
int tmp;
|
||||||
RelayConfigs::GetConfigs()->GetValueFor( "USE_B64", &tmp );
|
RelayConfigs::GetConfigs()->GetValueFor( "USE_B64", &tmp );
|
||||||
m_useB64 = tmp != 0;
|
m_useB64 = tmp != 0;
|
||||||
|
logf( XW_LOGINFO, "%s: m_useB64=%d", __func__, m_useB64 );
|
||||||
|
|
||||||
pthread_key_create( &m_conn_key, destr_function );
|
pthread_key_create( &m_conn_key, destr_function );
|
||||||
|
|
||||||
|
@ -123,6 +124,39 @@ DBMgr::AddNew( const char* cookie, const char* connName, CookieID cid,
|
||||||
PQclear( result );
|
PQclear( result );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Grab the row for a connname. If the params don't check out, return false.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
DBMgr::FindGameFor( const char* connName, char* cookieBuf, int bufLen,
|
||||||
|
unsigned short seed, HostID hid,
|
||||||
|
int nPlayersH, int nPlayersS,
|
||||||
|
int* langP, bool* isDead, CookieID* cidp )
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
const char* fmt = "SELECT cid, room, lang, nPerDevice, dead FROM "
|
||||||
|
GAMES_TABLE " WHERE connName = '%s' AND nTotal = %d "
|
||||||
|
"AND %d = seeds[%d] AND 'A' = ack[%d] "
|
||||||
|
;
|
||||||
|
string query;
|
||||||
|
string_printf( query, fmt, connName, nPlayersS, seed, hid, hid );
|
||||||
|
logf( XW_LOGINFO, "query: %s", query.c_str() );
|
||||||
|
|
||||||
|
PGresult* result = PQexec( getThreadConn(), query.c_str() );
|
||||||
|
assert( 1 >= PQntuples( result ) );
|
||||||
|
found = 1 == PQntuples( result );
|
||||||
|
if ( found ) {
|
||||||
|
*cidp = atoi( PQgetvalue( result, 0, 0 ) );
|
||||||
|
snprintf( cookieBuf, bufLen, "%s", PQgetvalue( result, 0, 1 ) );
|
||||||
|
*langP = atoi( PQgetvalue( result, 0, 2 ) );
|
||||||
|
*isDead = 't' == PQgetvalue( result, 0, 4 )[0];
|
||||||
|
}
|
||||||
|
PQclear( result );
|
||||||
|
|
||||||
|
logf( XW_LOGINFO, "%s(%s)=>%d", __func__, connName, found );
|
||||||
|
return found;
|
||||||
|
} /* FindGameFor */
|
||||||
|
|
||||||
CookieID
|
CookieID
|
||||||
DBMgr::FindGame( const char* connName, char* cookieBuf, int bufLen,
|
DBMgr::FindGame( const char* connName, char* cookieBuf, int bufLen,
|
||||||
int* langP, int* nPlayersTP, int* nPlayersHP, bool* isDead )
|
int* langP, int* nPlayersTP, int* nPlayersHP, bool* isDead )
|
||||||
|
@ -131,12 +165,14 @@ DBMgr::FindGame( const char* connName, char* cookieBuf, int bufLen,
|
||||||
|
|
||||||
const char* fmt = "SELECT cid, room, lang, nTotal, nPerDevice, dead FROM "
|
const char* fmt = "SELECT cid, room, lang, nTotal, nPerDevice, dead FROM "
|
||||||
GAMES_TABLE " WHERE connName = '%s'"
|
GAMES_TABLE " WHERE connName = '%s'"
|
||||||
" LIMIT 1";
|
// " LIMIT 1"
|
||||||
|
;
|
||||||
string query;
|
string query;
|
||||||
string_printf( query, fmt, connName );
|
string_printf( query, fmt, connName );
|
||||||
logf( XW_LOGINFO, "query: %s", query.c_str() );
|
logf( XW_LOGINFO, "query: %s", query.c_str() );
|
||||||
|
|
||||||
PGresult* result = PQexec( getThreadConn(), query.c_str() );
|
PGresult* result = PQexec( getThreadConn(), query.c_str() );
|
||||||
|
assert( 1 >= PQntuples( result ) );
|
||||||
if ( 1 == PQntuples( result ) ) {
|
if ( 1 == PQntuples( result ) ) {
|
||||||
cid = atoi( PQgetvalue( result, 0, 0 ) );
|
cid = atoi( PQgetvalue( result, 0, 0 ) );
|
||||||
snprintf( cookieBuf, bufLen, "%s", PQgetvalue( result, 0, 1 ) );
|
snprintf( cookieBuf, bufLen, "%s", PQgetvalue( result, 0, 1 ) );
|
||||||
|
@ -324,10 +360,10 @@ DBMgr::RegisterDevice( const DevID* host )
|
||||||
for ( success = false, ii = 0; !success; ++ii ) {
|
for ( success = false, ii = 0; !success; ++ii ) {
|
||||||
assert( 10 > ii ); // better to check that we're looping BECAUSE
|
assert( 10 > ii ); // better to check that we're looping BECAUSE
|
||||||
// of uniqueness problem.
|
// of uniqueness problem.
|
||||||
devID = (DevIDRelay)random();
|
do {
|
||||||
if ( DEVID_NONE == devID ) {
|
devID = (DevIDRelay)random();
|
||||||
continue;
|
} while ( DEVID_NONE == devID );
|
||||||
}
|
|
||||||
const char* command = "INSERT INTO " DEVICES_TABLE
|
const char* command = "INSERT INTO " DEVICES_TABLE
|
||||||
" (id, devType, devid)"
|
" (id, devType, devid)"
|
||||||
" VALUES( $1, $2, $3 )";
|
" VALUES( $1, $2, $3 )";
|
||||||
|
@ -383,13 +419,26 @@ DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion,
|
||||||
HostID newID = curID;
|
HostID newID = curID;
|
||||||
|
|
||||||
if ( newID == HOST_ID_NONE ) {
|
if ( newID == HOST_ID_NONE ) {
|
||||||
int arr[4] = {0};
|
int ackArr[4] = {0};
|
||||||
readArray( connName, arr );
|
int seedArr[4] = {0};
|
||||||
|
readArray( connName, "nPerDevice", ackArr );
|
||||||
|
readArray( connName, "seeds", seedArr );
|
||||||
|
|
||||||
|
// If our seed's already there, grab that slot. Otherwise grab the
|
||||||
|
// first empty one.
|
||||||
|
HostID firstEmpty = HOST_ID_NONE;
|
||||||
for ( newID = HOST_ID_SERVER; newID <= 4; ++newID ) {
|
for ( newID = HOST_ID_SERVER; newID <= 4; ++newID ) {
|
||||||
if ( arr[newID-1] == 0 ) {
|
if ( seedArr[newID-1] == seed ) {
|
||||||
break;
|
break;
|
||||||
|
} else if ( HOST_ID_NONE == firstEmpty && 0 == ackArr[newID-1] ) {
|
||||||
|
firstEmpty = newID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( 4 < newID && HOST_ID_NONE != firstEmpty ) {
|
||||||
|
newID = firstEmpty;
|
||||||
|
}
|
||||||
|
logf( XW_LOGINFO, "%s: set newID = %d", __func__, newID );
|
||||||
}
|
}
|
||||||
assert( newID <= 4 );
|
assert( newID <= 4 );
|
||||||
|
|
||||||
|
@ -537,10 +586,10 @@ DBMgr::RecordSent( const char* const connName, HostID hid, int nBytes )
|
||||||
{
|
{
|
||||||
assert( hid >= 0 && hid <= 4 );
|
assert( hid >= 0 && hid <= 4 );
|
||||||
const char* fmt = "UPDATE " GAMES_TABLE " SET"
|
const char* fmt = "UPDATE " GAMES_TABLE " SET"
|
||||||
" nsent = nsent + %d, mtimes[%d] = 'now'"
|
" nsents[%d] = nsents[%d] + %d, mtimes[%d] = 'now'"
|
||||||
" WHERE connName = '%s'";
|
" WHERE connName = '%s'";
|
||||||
string query;
|
string query;
|
||||||
string_printf( query, fmt, nBytes, hid, connName );
|
string_printf( query, fmt, hid, hid, nBytes, hid, connName );
|
||||||
logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() );
|
logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() );
|
||||||
|
|
||||||
execSql( query );
|
execSql( query );
|
||||||
|
@ -674,8 +723,15 @@ DBMgr::TokenFor( const char* const connName, int hid, DevIDRelay* devid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PQclear( result );
|
PQclear( result );
|
||||||
logf( XW_LOGINFO, "%s(%s,%d)=>%s (%d, %d)", __func__, connName, hid,
|
|
||||||
(found?"true":"false"), *devid, *token );
|
|
||||||
|
if ( found ) {
|
||||||
|
logf( XW_LOGINFO, "%s(%s,%d)=>true (%d, %d)", __func__, connName, hid,
|
||||||
|
*devid, *token );
|
||||||
|
} else {
|
||||||
|
logf( XW_LOGINFO, "%s(%s,%d)=>false", __func__, connName, hid );
|
||||||
|
}
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -699,27 +755,36 @@ DBMgr::execSql( const string& query )
|
||||||
bool
|
bool
|
||||||
DBMgr::execSql( const char* const query )
|
DBMgr::execSql( const char* const query )
|
||||||
{
|
{
|
||||||
PGresult* result = PQexec( getThreadConn(), query );
|
bool ok = false;
|
||||||
bool ok = PGRES_COMMAND_OK == PQresultStatus(result);
|
for ( int ii = 0; !ok && ii < 3; ++ii ) {
|
||||||
if ( !ok ) {
|
PGresult* result = PQexec( getThreadConn(), query );
|
||||||
logf( XW_LOGERROR, "PQexec=>%s;%s", PQresStatus(PQresultStatus(result)), PQresultErrorMessage(result) );
|
ok = PGRES_COMMAND_OK == PQresultStatus(result);
|
||||||
|
if ( !ok ) {
|
||||||
|
logf( XW_LOGERROR, "%s: PQexec=>%s;%s", __func__,
|
||||||
|
PQresStatus(PQresultStatus(result)),
|
||||||
|
PQresultErrorMessage(result) );
|
||||||
|
clearThreadConn();
|
||||||
|
usleep( 20000 );
|
||||||
|
}
|
||||||
|
PQclear( result );
|
||||||
}
|
}
|
||||||
PQclear( result );
|
assert( ok );
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
DBMgr::readArray( const char* const connName, int arr[] ) /* len 4 */
|
DBMgr::readArray( const char* const connName, const char* column, int arr[] ) /* len 4 */
|
||||||
{
|
{
|
||||||
const char* fmt = "SELECT nPerDevice FROM " GAMES_TABLE " WHERE connName='%s'";
|
const char* fmt = "SELECT %s FROM " GAMES_TABLE " WHERE connName='%s'";
|
||||||
|
|
||||||
string query;
|
string query;
|
||||||
string_printf( query, fmt, connName );
|
string_printf( query, fmt, column, connName );
|
||||||
logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() );
|
logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() );
|
||||||
|
|
||||||
PGresult* result = PQexec( getThreadConn(), query.c_str() );
|
PGresult* result = PQexec( getThreadConn(), query.c_str() );
|
||||||
assert( 1 == PQntuples( result ) );
|
assert( 1 == PQntuples( result ) );
|
||||||
const char* arrStr = PQgetvalue( result, 0, 0 );
|
const char* arrStr = PQgetvalue( result, 0, 0 );
|
||||||
|
logf( XW_LOGINFO, "%s: arrStr=\"%s\"", __func__, arrStr );
|
||||||
sscanf( arrStr, "{%d,%d,%d,%d}", &arr[0], &arr[1], &arr[2], &arr[3] );
|
sscanf( arrStr, "{%d,%d,%d,%d}", &arr[0], &arr[1], &arr[2], &arr[3] );
|
||||||
PQclear( result );
|
PQclear( result );
|
||||||
}
|
}
|
||||||
|
@ -727,15 +792,16 @@ DBMgr::readArray( const char* const connName, int arr[] ) /* len 4 */
|
||||||
DevIDRelay
|
DevIDRelay
|
||||||
DBMgr::getDevID( const char* connName, int hid )
|
DBMgr::getDevID( const char* connName, int hid )
|
||||||
{
|
{
|
||||||
DevIDRelay devID;
|
DevIDRelay devID = DEVID_NONE;
|
||||||
const char* fmt = "SELECT devids[%d] FROM " GAMES_TABLE " WHERE connName='%s'";
|
const char* fmt = "SELECT devids[%d] FROM " GAMES_TABLE " WHERE connName='%s'";
|
||||||
string query;
|
string query;
|
||||||
string_printf( query, fmt, hid, connName );
|
string_printf( query, fmt, hid, connName );
|
||||||
logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() );
|
logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() );
|
||||||
|
|
||||||
PGresult* result = PQexec( getThreadConn(), query.c_str() );
|
PGresult* result = PQexec( getThreadConn(), query.c_str() );
|
||||||
assert( 1 == PQntuples( result ) );
|
if ( 1 == PQntuples( result ) ) {
|
||||||
devID = (DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 );
|
devID = (DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 );
|
||||||
|
}
|
||||||
PQclear( result );
|
PQclear( result );
|
||||||
return devID;
|
return devID;
|
||||||
}
|
}
|
||||||
|
@ -755,20 +821,22 @@ DBMgr::getDevID( const DevID* devID )
|
||||||
string_printf( query, fmt, cur );
|
string_printf( query, fmt, cur );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const char* fmt = "SELECT id FROM " DEVICES_TABLE " WHERE devtype=%d and devid = '%s'";
|
const char* fmt = "SELECT id FROM " DEVICES_TABLE
|
||||||
|
" WHERE devtype=%d and devid = '%s'";
|
||||||
string_printf( query, fmt, devIDType, devID->m_devIDString.c_str() );
|
string_printf( query, fmt, devIDType, devID->m_devIDString.c_str() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( 0 < query.size() ) {
|
if ( 0 < query.size() ) {
|
||||||
logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() );
|
logf( XW_LOGINFO, "%s: query: %s", __func__, query.c_str() );
|
||||||
PGresult* result = PQexec( getThreadConn(), query.c_str() );
|
PGresult* result = PQexec( getThreadConn(), query.c_str() );
|
||||||
assert( 1 >= PQntuples( result ) );
|
int nTuples = PQntuples( result );
|
||||||
if ( 1 == PQntuples( result ) ) {
|
assert( 1 >= nTuples );
|
||||||
|
if ( 1 == nTuples ) {
|
||||||
rDevID = (DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 );
|
rDevID = (DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 );
|
||||||
}
|
}
|
||||||
PQclear( result );
|
PQclear( result );
|
||||||
}
|
}
|
||||||
logf( XW_LOGINFO, "%s(in=%s)=>%d (0x.8X)", __func__,
|
logf( XW_LOGINFO, "%s(in=%s)=>%d (0x%.8X)", __func__,
|
||||||
devID->m_devIDString.c_str(), rDevID, rDevID );
|
devID->m_devIDString.c_str(), rDevID, rDevID );
|
||||||
return rDevID;
|
return rDevID;
|
||||||
}
|
}
|
||||||
|
@ -839,11 +907,15 @@ DBMgr::StoreMessage( const char* const connName, int hid,
|
||||||
const unsigned char* buf, int len )
|
const unsigned char* buf, int len )
|
||||||
{
|
{
|
||||||
DevIDRelay devID = getDevID( connName, hid );
|
DevIDRelay devID = getDevID( connName, hid );
|
||||||
|
if ( DEVID_NONE == devID ) {
|
||||||
|
logf( XW_LOGERROR, "%s: warning: devid not found for connName=%s, hid=%d",
|
||||||
|
__func__, connName, hid );
|
||||||
|
}
|
||||||
|
|
||||||
size_t newLen;
|
size_t newLen;
|
||||||
const char* fmt = "INSERT INTO " MSGS_TABLE
|
const char* fmt = "INSERT INTO " MSGS_TABLE " "
|
||||||
" (connname, hid, devid, token, %s, msglen)"
|
"(connname, hid, devid, token, %s, msglen) "
|
||||||
" VALUES( '%s', %d, %d, "
|
"VALUES( '%s', %d, %d, "
|
||||||
"(SELECT tokens[%d] from " GAMES_TABLE " where connname='%s'), "
|
"(SELECT tokens[%d] from " GAMES_TABLE " where connname='%s'), "
|
||||||
"%s'%s', %d)";
|
"%s'%s', %d)";
|
||||||
|
|
||||||
|
@ -855,7 +927,7 @@ DBMgr::StoreMessage( const char* const connName, int hid,
|
||||||
g_free( b64 );
|
g_free( b64 );
|
||||||
} else {
|
} else {
|
||||||
unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf,
|
unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf,
|
||||||
len, &newLen );
|
len, &newLen );
|
||||||
assert( NULL != bytes );
|
assert( NULL != bytes );
|
||||||
|
|
||||||
string_printf( query, fmt, "msg", connName, hid, devID, hid, connName,
|
string_printf( query, fmt, "msg", connName, hid, devID, hid, connName,
|
||||||
|
@ -1028,7 +1100,6 @@ DBMgr::getCountWhere( const char* table, string& test )
|
||||||
assert( 1 == PQntuples( result ) );
|
assert( 1 == PQntuples( result ) );
|
||||||
int count = atoi( PQgetvalue( result, 0, 0 ) );
|
int count = atoi( PQgetvalue( result, 0, 0 ) );
|
||||||
PQclear( result );
|
PQclear( result );
|
||||||
logf( XW_LOGINFO, "%s(%s)=>%d", __func__, query.c_str(), count );
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1073,6 +1144,18 @@ destr_function( void* conn )
|
||||||
PQfinish( pgconn );
|
PQfinish( pgconn );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DBMgr::clearThreadConn()
|
||||||
|
{
|
||||||
|
logf( XW_LOGERROR, "%s called()", __func__ );
|
||||||
|
PGconn* conn = (PGconn*)pthread_getspecific( m_conn_key );
|
||||||
|
if ( NULL != conn ) {
|
||||||
|
PQfinish( conn );
|
||||||
|
int result = pthread_setspecific( m_conn_key, NULL );
|
||||||
|
assert( 0 == result );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PGconn*
|
PGconn*
|
||||||
DBMgr::getThreadConn( void )
|
DBMgr::getThreadConn( void )
|
||||||
{
|
{
|
||||||
|
@ -1080,12 +1163,19 @@ DBMgr::getThreadConn( void )
|
||||||
|
|
||||||
if ( NULL == conn ) {
|
if ( NULL == conn ) {
|
||||||
char buf[128];
|
char buf[128];
|
||||||
int len = snprintf( buf, sizeof(buf), "dbname = " );
|
int port;
|
||||||
if ( !RelayConfigs::GetConfigs()->
|
if ( !RelayConfigs::GetConfigs()->GetValueFor( "DB_NAME", buf,
|
||||||
GetValueFor( "DB_NAME", &buf[len], sizeof(buf)-len ) ) {
|
sizeof(buf) ) ) {
|
||||||
assert( 0 );
|
assert( 0 );
|
||||||
}
|
}
|
||||||
conn = PQconnectdb( buf );
|
if ( !RelayConfigs::GetConfigs()->GetValueFor( "DB_PORT", &port ) ) {
|
||||||
|
assert( 0 );
|
||||||
|
}
|
||||||
|
string params;
|
||||||
|
string_printf( params, "dbname = %s ", buf );
|
||||||
|
string_printf( params, "port = %d ", port );
|
||||||
|
|
||||||
|
conn = PQconnectdb( params.c_str() );
|
||||||
pthread_setspecific( m_conn_key, conn );
|
pthread_setspecific( m_conn_key, conn );
|
||||||
}
|
}
|
||||||
return conn;
|
return conn;
|
||||||
|
|
|
@ -54,6 +54,11 @@ class DBMgr {
|
||||||
int* langP, int* nPlayersTP, int* nPlayersHP,
|
int* langP, int* nPlayersTP, int* nPlayersHP,
|
||||||
bool* isDead );
|
bool* isDead );
|
||||||
|
|
||||||
|
bool FindGameFor( const char* connName, char* cookieBuf, int bufLen,
|
||||||
|
unsigned short seed, HostID hid,
|
||||||
|
int nPlayersH, int nPlayersS,
|
||||||
|
int* langP, bool* isDead, CookieID* cidp );
|
||||||
|
|
||||||
bool SeenSeed( const char* cookie, unsigned short seed,
|
bool SeenSeed( const char* cookie, unsigned short seed,
|
||||||
int langCode, int nPlayersT, bool wantsPublic,
|
int langCode, int nPlayersT, bool wantsPublic,
|
||||||
char* connNameBuf, int bufLen, int* nPlayersHP,
|
char* connNameBuf, int bufLen, int* nPlayersHP,
|
||||||
|
@ -121,7 +126,7 @@ class DBMgr {
|
||||||
DBMgr();
|
DBMgr();
|
||||||
bool execSql( const string& query );
|
bool execSql( const string& query );
|
||||||
bool execSql( const char* const query ); /* no-results query */
|
bool execSql( const char* const query ); /* no-results query */
|
||||||
void readArray( const char* const connName, int arr[] );
|
void readArray( const char* const connName, const char* column, int arr[] );
|
||||||
DevIDRelay getDevID( const char* connName, int hid );
|
DevIDRelay getDevID( const char* connName, int hid );
|
||||||
DevIDRelay getDevID( const DevID* devID );
|
DevIDRelay getDevID( const DevID* devID );
|
||||||
int getCountWhere( const char* table, string& test );
|
int getCountWhere( const char* table, string& test );
|
||||||
|
@ -130,6 +135,7 @@ class DBMgr {
|
||||||
int byteaIndex, unsigned char* buf, size_t* buflen );
|
int byteaIndex, unsigned char* buf, size_t* buflen );
|
||||||
|
|
||||||
PGconn* getThreadConn( void );
|
PGconn* getThreadConn( void );
|
||||||
|
void clearThreadConn();
|
||||||
|
|
||||||
void conn_key_alloc();
|
void conn_key_alloc();
|
||||||
pthread_key_t m_conn_key;
|
pthread_key_t m_conn_key;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
|
@ -144,8 +144,8 @@ do_rooms( int sockfd, int lang, int nPlayers )
|
||||||
{
|
{
|
||||||
unsigned char msg[] = { 0, /* protocol */
|
unsigned char msg[] = { 0, /* protocol */
|
||||||
PRX_PUB_ROOMS,
|
PRX_PUB_ROOMS,
|
||||||
lang,
|
(unsigned char)lang,
|
||||||
nPlayers };
|
(unsigned char)nPlayers };
|
||||||
unsigned short len = htons( sizeof(msg) );
|
unsigned short len = htons( sizeof(msg) );
|
||||||
write( sockfd, &len, sizeof(len) );
|
write( sockfd, &len, sizeof(len) );
|
||||||
write( sockfd, msg, sizeof(msg) );
|
write( sockfd, msg, sizeof(msg) );
|
||||||
|
@ -184,7 +184,7 @@ write_connnames( int sockfd, char cmd,
|
||||||
len += 1 + strlen( connNames[ii] );
|
len += 1 + strlen( connNames[ii] );
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char hdr[] = { 0, cmd };
|
unsigned char hdr[] = { 0, (unsigned char)cmd };
|
||||||
unsigned short netNConnNames = htons( nConnNames );
|
unsigned short netNConnNames = htons( nConnNames );
|
||||||
netlen = sizeof(hdr) + sizeof( netNConnNames ) + len;
|
netlen = sizeof(hdr) + sizeof( netNConnNames ) + len;
|
||||||
netlen = htons( netlen );
|
netlen = htons( netlen );
|
||||||
|
@ -234,6 +234,22 @@ connect_socket( void )
|
||||||
to_sock.sin_family = AF_INET;
|
to_sock.sin_family = AF_INET;
|
||||||
to_sock.sin_port = htons( g_port );
|
to_sock.sin_port = htons( g_port );
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = 5; /* seconds */
|
||||||
|
tv.tv_usec = 0; /* microseconds */
|
||||||
|
|
||||||
|
int result = setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv) );
|
||||||
|
if ( 0 != result ) {
|
||||||
|
fprintf( stderr, "setsockopt=>%d (%s)", errno, strerror(errno) );
|
||||||
|
assert( 0 );
|
||||||
|
}
|
||||||
|
result = setsockopt( sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv) );
|
||||||
|
if ( 0 != result ) {
|
||||||
|
fprintf( stderr, "setsockopt=>%d (%s)", errno, strerror(errno) );
|
||||||
|
assert( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct hostent* hostip;
|
struct hostent* hostip;
|
||||||
hostip = gethostbyname( g_host );
|
hostip = gethostbyname( g_host );
|
||||||
memcpy( &(to_sock.sin_addr.s_addr), hostip->h_addr_list[0],
|
memcpy( &(to_sock.sin_addr.s_addr), hostip->h_addr_list[0],
|
||||||
|
@ -277,6 +293,7 @@ do_fetch( int sockfd, const char** connNames, int nConnNames,
|
||||||
unsigned char reply[1024];
|
unsigned char reply[1024];
|
||||||
int nRead = read_packet( sockfd, reply, sizeof(reply) );
|
int nRead = read_packet( sockfd, reply, sizeof(reply) );
|
||||||
if ( nRead > 2 ) {
|
if ( nRead > 2 ) {
|
||||||
|
int ii;
|
||||||
const unsigned char* bufp = reply;
|
const unsigned char* bufp = reply;
|
||||||
const unsigned char* const end = bufp + nRead;
|
const unsigned char* const end = bufp + nRead;
|
||||||
|
|
||||||
|
@ -292,7 +309,7 @@ do_fetch( int sockfd, const char** connNames, int nConnNames,
|
||||||
STDOUT -- e.g. by passing in named pipes to correspond to each
|
STDOUT -- e.g. by passing in named pipes to correspond to each
|
||||||
deviceid provided */
|
deviceid provided */
|
||||||
|
|
||||||
for ( int ii = 0; ii < count && bufp < end; ++ii ) {
|
for ( ii = 0; ii < count && bufp < end; ++ii ) {
|
||||||
int fd = STDOUT_FILENO;
|
int fd = STDOUT_FILENO;
|
||||||
int nbsfd = -1;
|
int nbsfd = -1;
|
||||||
unsigned short countPerDev;
|
unsigned short countPerDev;
|
||||||
|
@ -350,7 +367,6 @@ do_fetch( int sockfd, const char** connNames, int nConnNames,
|
||||||
nwritten = write( fd, &len, sizeof(len) );
|
nwritten = write( fd, &len, sizeof(len) );
|
||||||
assert( nwritten == sizeof(len) );
|
assert( nwritten == sizeof(len) );
|
||||||
|
|
||||||
int ii;
|
|
||||||
for ( ii = 0; -1 != nbsfd; ++ii ) {
|
for ( ii = 0; -1 != nbsfd; ++ii ) {
|
||||||
short len;
|
short len;
|
||||||
ssize_t nRead = read( nbsfd, &len, sizeof(len) );
|
ssize_t nRead = read( nbsfd, &len, sizeof(len) );
|
||||||
|
|
|
@ -112,7 +112,7 @@ def asGCMIds(con, devids, typ):
|
||||||
def notifyGCM( devids, typ, target ):
|
def notifyGCM( devids, typ, target ):
|
||||||
success = False
|
success = False
|
||||||
if typ == DEVTYPE_GCM:
|
if typ == DEVTYPE_GCM:
|
||||||
if 3 <= target['clntVers']:
|
if 3 <= target['clntVers'] and target['msg64']:
|
||||||
connname = "%s/%d" % (target['connname'], target['hid'])
|
connname = "%s/%d" % (target['connname'], target['hid'])
|
||||||
data = { 'msgs64': [ target['msg64'] ],
|
data = { 'msgs64': [ target['msg64'] ],
|
||||||
'connname': connname,
|
'connname': connname,
|
||||||
|
@ -235,8 +235,9 @@ def main():
|
||||||
toDelete = []
|
toDelete = []
|
||||||
for devid in targets.keys():
|
for devid in targets.keys():
|
||||||
target = targets[devid]
|
target = targets[devid]
|
||||||
if notifyGCM( asGCMIds(g_con, [devid], typ), typ, target )\
|
if notifyGCM( asGCMIds(g_con, [devid], typ), typ, target ) \
|
||||||
and 3 <= target['clntVers']:
|
and 3 <= target['clntVers'] \
|
||||||
|
and target['msg64']:
|
||||||
toDelete.append( str(target['id']) )
|
toDelete.append( str(target['id']) )
|
||||||
pruneSent( devids )
|
pruneSent( devids )
|
||||||
deleteMsgs( g_con, toDelete )
|
deleteMsgs( g_con, toDelete )
|
||||||
|
|
|
@ -99,7 +99,7 @@ $cols = array( new Column("dead", "D", "capitalize", false ),
|
||||||
new Column("nperdevice", "NP", "identity", true ),
|
new Column("nperdevice", "NP", "identity", true ),
|
||||||
new Column("ack", "A", "identity", true ),
|
new Column("ack", "A", "identity", true ),
|
||||||
new Column("devids", "DevIDs", "identity", true ),
|
new Column("devids", "DevIDs", "identity", true ),
|
||||||
new Column("nsent", "Sent", "identity", false ),
|
new Column("nsents", "Sent", "identity", true ),
|
||||||
new Column("addrs", "Dev. addr", "ip_to_host", true ),
|
new Column("addrs", "Dev. addr", "ip_to_host", true ),
|
||||||
new Column("ctime", "Created", "print_date", false ),
|
new Column("ctime", "Created", "print_date", false ),
|
||||||
new Column("mtimes", "Last contact", "print_date", true ),
|
new Column("mtimes", "Last contact", "print_date", true ),
|
||||||
|
|
|
@ -27,7 +27,7 @@ echo -n "Device (pid) count: $(pidof xwords | wc | awk '{print $2}')"
|
||||||
echo "; relay pid[s]: $(pidof xwrelay)"
|
echo "; relay pid[s]: $(pidof xwrelay)"
|
||||||
echo "Row count:" $(psql -t xwgames -c "select count(*) FROM games $QUERY;")
|
echo "Row count:" $(psql -t xwgames -c "select count(*) FROM games $QUERY;")
|
||||||
|
|
||||||
echo "SELECT dead,connname,cid,room,lang,clntVers as cv ,ntotal,nperdevice,seeds,addrs,tokens,devids,ack,nsent as snt "\
|
echo "SELECT dead,connname,cid,room,lang as lg,clntVers as cv ,ntotal as tot,nperdevice as nPerDev,seeds,tokens,ack,nsents as snts "\
|
||||||
"FROM games $QUERY ORDER BY NOT dead, connname LIMIT $LIMIT;" \
|
"FROM games $QUERY ORDER BY NOT dead, connname LIMIT $LIMIT;" \
|
||||||
| psql xwgames
|
| psql xwgames
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
TimerMgr::TimerMgr()
|
TimerMgr::TimerMgr()
|
||||||
: m_nextFireTime(0)
|
: m_nextFireTime(0)
|
||||||
|
,m_nextID(0)
|
||||||
{
|
{
|
||||||
pthread_mutex_init( &m_timersMutex, NULL );
|
pthread_mutex_init( &m_timersMutex, NULL );
|
||||||
}
|
}
|
||||||
|
@ -44,17 +45,18 @@ TimerMgr::GetTimerMgr()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimerMgr::SetTimer( time_t inMillis, TimerProc proc, void* closure,
|
TimerMgr::SetTimer( time_t inSeconds, TimerProc proc, void* closure,
|
||||||
int interval )
|
int interval )
|
||||||
{
|
{
|
||||||
logf( XW_LOGINFO, "%s: uptime = %ld", __func__, uptime() );
|
logf( XW_LOGINFO, "%s: uptime = %ld", __func__, uptime() );
|
||||||
TimerInfo ti;
|
TimerInfo ti;
|
||||||
ti.proc = proc;
|
ti.proc = proc;
|
||||||
ti.closure = closure;
|
ti.closure = closure;
|
||||||
ti.when = uptime() + inMillis;
|
ti.when = uptime() + inSeconds;
|
||||||
ti.interval = interval;
|
ti.interval = interval;
|
||||||
|
|
||||||
MutexLock ml( &m_timersMutex );
|
MutexLock ml( &m_timersMutex );
|
||||||
|
ti.id = ++m_nextID;
|
||||||
|
|
||||||
if ( getTimer( proc, closure ) ) {
|
if ( getTimer( proc, closure ) ) {
|
||||||
logf( XW_LOGINFO, "%s: clearing old timer", __func__ );
|
logf( XW_LOGINFO, "%s: clearing old timer", __func__ );
|
||||||
|
@ -68,7 +70,7 @@ TimerMgr::SetTimer( time_t inMillis, TimerProc proc, void* closure,
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t
|
time_t
|
||||||
TimerMgr::GetPollTimeout()
|
TimerMgr::GetPollTimeoutMillis()
|
||||||
{
|
{
|
||||||
MutexLock ml( &m_timersMutex );
|
MutexLock ml( &m_timersMutex );
|
||||||
|
|
||||||
|
@ -80,10 +82,10 @@ TimerMgr::GetPollTimeout()
|
||||||
if ( tout < 0 ) {
|
if ( tout < 0 ) {
|
||||||
tout = 0;
|
tout = 0;
|
||||||
}
|
}
|
||||||
tout *= 1000;
|
tout *= 1000; /* convert to milliseconds */
|
||||||
}
|
}
|
||||||
return tout;
|
return tout;
|
||||||
} /* GetPollTimeout */
|
} /* GetPollTimeoutMillis */
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TimerMgr::getTimer( TimerProc proc, void* closure )
|
TimerMgr::getTimer( TimerProc proc, void* closure )
|
||||||
|
@ -145,6 +147,7 @@ TimerMgr::FireElapsedTimers()
|
||||||
|
|
||||||
vector<TimerProc> procs;
|
vector<TimerProc> procs;
|
||||||
vector<void*> closures;
|
vector<void*> closures;
|
||||||
|
vector<uint32_t> ids;
|
||||||
{
|
{
|
||||||
MutexLock ml( &m_timersMutex );
|
MutexLock ml( &m_timersMutex );
|
||||||
/* loop until we get through without firing a single one. Only fire one
|
/* loop until we get through without firing a single one. Only fire one
|
||||||
|
@ -157,6 +160,7 @@ TimerMgr::FireElapsedTimers()
|
||||||
|
|
||||||
procs.push_back(tip->proc);
|
procs.push_back(tip->proc);
|
||||||
closures.push_back(tip->closure);
|
closures.push_back(tip->closure);
|
||||||
|
ids.push_back(tip->id);
|
||||||
|
|
||||||
if ( tip->interval ) {
|
if ( tip->interval ) {
|
||||||
tip->when += tip->interval;
|
tip->when += tip->interval;
|
||||||
|
@ -167,10 +171,12 @@ TimerMgr::FireElapsedTimers()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<TimerProc>::iterator iter1 = procs.begin();
|
vector<TimerProc>::const_iterator procs_iter = procs.begin();
|
||||||
vector<void*>::iterator iter2 = closures.begin();
|
vector<void*>::const_iterator closures_iter = closures.begin();
|
||||||
while ( iter1 != procs.end() ) {
|
vector<uint32_t>::const_iterator ids_iter = ids.begin();
|
||||||
(*iter1++)(*iter2++);
|
while ( procs_iter != procs.end() ) {
|
||||||
|
logf( XW_LOGINFO, "%s: firing timer id=%d", __func__, *ids_iter++ );
|
||||||
|
(*procs_iter++)(*closures_iter++);
|
||||||
}
|
}
|
||||||
|
|
||||||
MutexLock ml( &m_timersMutex );
|
MutexLock ml( &m_timersMutex );
|
||||||
|
@ -184,6 +190,7 @@ TimerMgr::clearTimerImpl( TimerProc proc, void* closure )
|
||||||
for ( iter = m_timers.begin(); iter != m_timers.end(); ++iter ) {
|
for ( iter = m_timers.begin(); iter != m_timers.end(); ++iter ) {
|
||||||
TimerInfo* tip = &(*iter);
|
TimerInfo* tip = &(*iter);
|
||||||
if ( tip->proc == proc && tip->closure == closure ) {
|
if ( tip->proc == proc && tip->closure == closure ) {
|
||||||
|
logf( XW_LOGINFO, "clearing timer id=%d", tip->id );
|
||||||
m_timers.erase(iter);
|
m_timers.erase(iter);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ class TimerMgr {
|
||||||
int interval ); /* 0 means non-recurring */
|
int interval ); /* 0 means non-recurring */
|
||||||
void ClearTimer( TimerProc proc, void* closure );
|
void ClearTimer( TimerProc proc, void* closure );
|
||||||
|
|
||||||
time_t GetPollTimeout();
|
time_t GetPollTimeoutMillis();
|
||||||
void FireElapsedTimers();
|
void FireElapsedTimers();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -51,6 +51,7 @@ class TimerMgr {
|
||||||
void* closure;
|
void* closure;
|
||||||
time_t when;
|
time_t when;
|
||||||
int interval;
|
int interval;
|
||||||
|
uint32_t id;
|
||||||
} TimerInfo;
|
} TimerInfo;
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,6 +66,7 @@ class TimerMgr {
|
||||||
list<TimerInfo> m_timers;
|
list<TimerInfo> m_timers;
|
||||||
|
|
||||||
time_t m_nextFireTime;
|
time_t m_nextFireTime;
|
||||||
|
uint32_t m_nextID;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -118,18 +118,36 @@ void
|
||||||
XWThreadPool::AddSocket( SockType stype, QueueCallback proc, const AddrInfo* from )
|
XWThreadPool::AddSocket( SockType stype, QueueCallback proc, const AddrInfo* from )
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
int sock = from->socket();
|
||||||
RWWriteLock ml( &m_activeSocketsRWLock );
|
RWWriteLock ml( &m_activeSocketsRWLock );
|
||||||
SockInfo si;
|
SockInfo si;
|
||||||
si.m_type = stype;
|
si.m_type = stype;
|
||||||
si.m_proc = proc;
|
si.m_proc = proc;
|
||||||
si.m_addr = *from;
|
si.m_addr = *from;
|
||||||
m_activeSockets.push_back( si );
|
m_activeSockets.insert( pair<int, SockInfo>( sock, si ) );
|
||||||
logf( XW_LOGINFO, "%s: %d sockets active", __func__,
|
logf( XW_LOGINFO, "%s(sock=%d): %d sockets active", __func__, sock,
|
||||||
m_activeSockets.size() );
|
m_activeSockets.size() );
|
||||||
}
|
}
|
||||||
interrupt_poll();
|
interrupt_poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
XWThreadPool::SocketFound( const AddrInfo* addr )
|
||||||
|
{
|
||||||
|
assert( addr->isTCP() );
|
||||||
|
bool found = false;
|
||||||
|
{
|
||||||
|
RWWriteLock ml( &m_activeSocketsRWLock );
|
||||||
|
|
||||||
|
map<int, SockInfo>::iterator iter = m_activeSockets.find( addr->socket() );
|
||||||
|
if ( m_activeSockets.end() != iter
|
||||||
|
&& iter->second.m_addr.equals( *addr ) ) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
XWThreadPool::RemoveSocket( const AddrInfo* addr )
|
XWThreadPool::RemoveSocket( const AddrInfo* addr )
|
||||||
{
|
{
|
||||||
|
@ -138,20 +156,15 @@ XWThreadPool::RemoveSocket( const AddrInfo* addr )
|
||||||
{
|
{
|
||||||
RWWriteLock ml( &m_activeSocketsRWLock );
|
RWWriteLock ml( &m_activeSocketsRWLock );
|
||||||
|
|
||||||
logf( XW_LOGINFO, "%s: START: %d sockets active", __func__,
|
size_t prevSize = m_activeSockets.size();
|
||||||
m_activeSockets.size() );
|
|
||||||
|
|
||||||
vector<SockInfo>::iterator iter;
|
map<int, SockInfo>::iterator iter = m_activeSockets.find( addr->socket() );
|
||||||
for ( iter = m_activeSockets.begin();
|
if ( m_activeSockets.end() != iter && iter->second.m_addr.equals( *addr ) ) {
|
||||||
iter != m_activeSockets.end(); ++iter ) {
|
m_activeSockets.erase( iter );
|
||||||
if ( iter->m_addr.equals( *addr ) ) {
|
found = true;
|
||||||
m_activeSockets.erase( iter );
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
logf( XW_LOGINFO, "%s: AFTER: %d sockets active", __func__,
|
logf( XW_LOGINFO, "%s: AFTER: %d sockets active (was %d)", __func__,
|
||||||
m_activeSockets.size() );
|
m_activeSockets.size(), prevSize );
|
||||||
}
|
}
|
||||||
return found;
|
return found;
|
||||||
} /* RemoveSocket */
|
} /* RemoveSocket */
|
||||||
|
@ -159,7 +172,6 @@ XWThreadPool::RemoveSocket( const AddrInfo* addr )
|
||||||
void
|
void
|
||||||
XWThreadPool::CloseSocket( const AddrInfo* addr )
|
XWThreadPool::CloseSocket( const AddrInfo* addr )
|
||||||
{
|
{
|
||||||
/* bool do_interrupt = false; */
|
|
||||||
assert( addr->isTCP() );
|
assert( addr->isTCP() );
|
||||||
if ( !RemoveSocket( addr ) ) {
|
if ( !RemoveSocket( addr ) ) {
|
||||||
MutexLock ml( &m_queueMutex );
|
MutexLock ml( &m_queueMutex );
|
||||||
|
@ -167,7 +179,6 @@ XWThreadPool::CloseSocket( const AddrInfo* addr )
|
||||||
while ( iter != m_queue.end() ) {
|
while ( iter != m_queue.end() ) {
|
||||||
if ( iter->m_info.m_addr.equals( *addr ) ) {
|
if ( iter->m_info.m_addr.equals( *addr ) ) {
|
||||||
m_queue.erase( iter );
|
m_queue.erase( iter );
|
||||||
/* do_interrupt = true; */
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++iter;
|
++iter;
|
||||||
|
@ -175,13 +186,12 @@ XWThreadPool::CloseSocket( const AddrInfo* addr )
|
||||||
}
|
}
|
||||||
logf( XW_LOGINFO, "CLOSING socket %d", addr->socket() );
|
logf( XW_LOGINFO, "CLOSING socket %d", addr->socket() );
|
||||||
close( addr->socket() );
|
close( addr->socket() );
|
||||||
/* if ( do_interrupt ) { */
|
|
||||||
/* We always need to interrupt the poll because the socket we're closing
|
/* We always need to interrupt the poll because the socket we're closing
|
||||||
will be in the list being listened to. That or we need to drop sockets
|
will be in the list being listened to. That or we need to drop sockets
|
||||||
that have been removed on some other thread while the poll call's
|
that have been removed on some other thread while the poll call's
|
||||||
blocking.*/
|
blocking.*/
|
||||||
interrupt_poll();
|
interrupt_poll();
|
||||||
/* } */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -196,29 +206,28 @@ XWThreadPool::EnqueueKill( const AddrInfo* addr, const char* const why )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return true if the addr passed in has a timestamp >= what we have as the
|
||||||
|
// creation time of the now-open socket. If the socket isn't open, return false.
|
||||||
bool
|
bool
|
||||||
XWThreadPool::get_process_packet( SockType stype, QueueCallback proc, const AddrInfo* addr )
|
XWThreadPool::IsCurrent( const AddrInfo* addr )
|
||||||
{
|
{
|
||||||
bool success = false;
|
bool result = false;
|
||||||
short packetSize;
|
bool sockFound = false; // for debugging
|
||||||
assert( sizeof(packetSize) == 2 );
|
int sock = addr->socket();
|
||||||
|
if ( -1 != sock ) {
|
||||||
// Fix this to return an allocated buffer
|
RWReadLock ml( &m_activeSocketsRWLock );
|
||||||
unsigned char buf[MAX_MSG_LEN+1];
|
map<int, SockInfo>::const_iterator iter = m_activeSockets.find( sock );
|
||||||
int nRead = read_packet( addr->socket(), buf, sizeof(buf) );
|
if ( iter != m_activeSockets.end() ) {
|
||||||
if ( nRead < 0 ) {
|
assert( !sockFound );
|
||||||
EnqueueKill( addr, "bad packet" );
|
sockFound = true;
|
||||||
} else if ( STYPE_PROXY == stype && NULL != proc ) {
|
result = iter->second.m_addr.created() <= addr->created();
|
||||||
buf[nRead] = '\0';
|
logf( XW_LOGINFO, "%s(sock=%d)=>%d (%lx vs %lx)",
|
||||||
UdpQueue::get()->handle( addr, buf, nRead+1, proc );
|
__func__, sock, result,
|
||||||
} else if ( STYPE_GAME == stype && NULL != proc ) {
|
iter->second.m_addr.created(), addr->created() );
|
||||||
UdpQueue::get()->handle( addr, buf, nRead, proc );
|
}
|
||||||
success = true;
|
|
||||||
} else {
|
|
||||||
assert(0);
|
|
||||||
}
|
}
|
||||||
return success;
|
return result;
|
||||||
} /* get_process_packet */
|
}
|
||||||
|
|
||||||
void*
|
void*
|
||||||
XWThreadPool::tpool_main( void* closure )
|
XWThreadPool::tpool_main( void* closure )
|
||||||
|
@ -261,10 +270,11 @@ XWThreadPool::real_tpool_main( ThreadInfo* tip )
|
||||||
logf( XW_LOGINFO, "worker thread got socket %d from queue", socket );
|
logf( XW_LOGINFO, "worker thread got socket %d from queue", socket );
|
||||||
switch ( pr.m_act ) {
|
switch ( pr.m_act ) {
|
||||||
case Q_READ:
|
case Q_READ:
|
||||||
assert( socket >= 0 );
|
assert( 0 );
|
||||||
if ( get_process_packet( pr.m_info.m_type, pr.m_info.m_proc, &pr.m_info.m_addr ) ) {
|
// assert( socket >= 0 );
|
||||||
AddSocket( pr.m_info.m_type, pr.m_info.m_proc, &pr.m_info.m_addr );
|
// if ( get_process_packet( pr.m_info.m_type, pr.m_info.m_proc, &pr.m_info.m_addr ) ) {
|
||||||
}
|
// AddSocket( pr.m_info.m_type, pr.m_info.m_proc, &pr.m_info.m_addr );
|
||||||
|
// }
|
||||||
break;
|
break;
|
||||||
case Q_KILL:
|
case Q_KILL:
|
||||||
(*m_kFunc)( &pr.m_info.m_addr );
|
(*m_kFunc)( &pr.m_info.m_addr );
|
||||||
|
@ -332,11 +342,11 @@ XWThreadPool::real_listener()
|
||||||
#endif
|
#endif
|
||||||
++curfd;
|
++curfd;
|
||||||
|
|
||||||
vector<SockInfo>::iterator iter;
|
map<int, SockInfo>::iterator iter;
|
||||||
for ( iter = m_activeSockets.begin(); iter != m_activeSockets.end();
|
for ( iter = m_activeSockets.begin(); iter != m_activeSockets.end();
|
||||||
++iter ) {
|
++iter ) {
|
||||||
fds[curfd].fd = iter->m_addr.socket();
|
fds[curfd].fd = iter->first;
|
||||||
sinfos[curfd] = *iter;
|
sinfos[curfd] = iter->second;
|
||||||
fds[curfd].events = flags;
|
fds[curfd].events = flags;
|
||||||
#ifdef LOG_POLL
|
#ifdef LOG_POLL
|
||||||
if ( logCapacity > logLen ) {
|
if ( logCapacity > logLen ) {
|
||||||
|
@ -349,7 +359,7 @@ XWThreadPool::real_listener()
|
||||||
}
|
}
|
||||||
pthread_rwlock_unlock( &m_activeSocketsRWLock );
|
pthread_rwlock_unlock( &m_activeSocketsRWLock );
|
||||||
|
|
||||||
int nMillis = tmgr->GetPollTimeout();
|
int nMillis = tmgr->GetPollTimeoutMillis();
|
||||||
|
|
||||||
#ifdef LOG_POLL
|
#ifdef LOG_POLL
|
||||||
logf( XW_LOGINFO, "polling %s nmillis=%d", log, nMillis );
|
logf( XW_LOGINFO, "polling %s nmillis=%d", log, nMillis );
|
||||||
|
@ -387,10 +397,12 @@ XWThreadPool::real_listener()
|
||||||
for ( ii = 0; ii < nSockets && nEvents > 0; ++ii ) {
|
for ( ii = 0; ii < nSockets && nEvents > 0; ++ii ) {
|
||||||
|
|
||||||
if ( fds[curfd].revents != 0 ) {
|
if ( fds[curfd].revents != 0 ) {
|
||||||
int socket = fds[curfd].fd;
|
// int socket = fds[curfd].fd;
|
||||||
const AddrInfo* addr = &sinfos[curfd].m_addr;
|
SockInfo* sinfo = &sinfos[curfd];
|
||||||
assert( socket == addr->socket() );
|
const AddrInfo* addr = &sinfo->m_addr;
|
||||||
if ( !RemoveSocket( addr ) ) {
|
|
||||||
|
assert( fds[curfd].fd == addr->socket() );
|
||||||
|
if ( !SocketFound( addr ) ) {
|
||||||
/* no further processing if it's been removed while
|
/* no further processing if it's been removed while
|
||||||
we've been sleeping in poll */
|
we've been sleeping in poll */
|
||||||
--nEvents;
|
--nEvents;
|
||||||
|
@ -398,10 +410,14 @@ XWThreadPool::real_listener()
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( 0 != (fds[curfd].revents & (POLLIN | POLLPRI)) ) {
|
if ( 0 != (fds[curfd].revents & (POLLIN | POLLPRI)) ) {
|
||||||
enqueue( sinfos[curfd] );
|
if ( !UdpQueue::get()->handle( addr, sinfo->m_proc ) ) {
|
||||||
|
RemoveSocket( addr );
|
||||||
|
EnqueueKill( addr, "bad packet" );
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logf( XW_LOGERROR, "odd revents: %x",
|
logf( XW_LOGERROR, "odd revents: %x",
|
||||||
fds[curfd].revents );
|
fds[curfd].revents );
|
||||||
|
RemoveSocket( addr );
|
||||||
EnqueueKill( addr, "error/hup in poll()" );
|
EnqueueKill( addr, "error/hup in poll()" );
|
||||||
}
|
}
|
||||||
--nEvents;
|
--nEvents;
|
||||||
|
@ -450,7 +466,8 @@ XWThreadPool::grab_elem_locked( QueuePr* prp )
|
||||||
for ( iter = m_queue.begin(); !found && iter != m_queue.end(); ++iter ) {
|
for ( iter = m_queue.begin(); !found && iter != m_queue.end(); ++iter ) {
|
||||||
int socket = iter->m_info.m_addr.socket();
|
int socket = iter->m_info.m_addr.socket();
|
||||||
/* If NOT found */
|
/* If NOT found */
|
||||||
if ( m_sockets_in_use.end() == m_sockets_in_use.find( socket ) ) {
|
if ( -1 != socket
|
||||||
|
&& m_sockets_in_use.end() == m_sockets_in_use.find( socket ) ) {
|
||||||
*prp = *iter;
|
*prp = *iter;
|
||||||
m_queue.erase( iter ); /* this was a double-free once! */
|
m_queue.erase( iter ); /* this was a double-free once! */
|
||||||
m_sockets_in_use.insert( socket );
|
m_sockets_in_use.insert( socket );
|
||||||
|
@ -481,10 +498,10 @@ XWThreadPool::print_in_use( void )
|
||||||
|
|
||||||
for ( iter = m_sockets_in_use.begin();
|
for ( iter = m_sockets_in_use.begin();
|
||||||
iter != m_sockets_in_use.end(); ++iter ) {
|
iter != m_sockets_in_use.end(); ++iter ) {
|
||||||
string_printf( str, "%d ", *iter );
|
string_printf( str, "%d ", *iter );
|
||||||
}
|
}
|
||||||
if ( 0 < str.size() ) {
|
if ( 0 < str.size() ) {
|
||||||
logf( XW_LOGINFO, "Sockets in use: %s", str.c_str() );
|
logf( XW_LOGINFO, "Sockets in use: %s", str.c_str() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,12 +68,16 @@ class XWThreadPool {
|
||||||
|
|
||||||
void EnqueueKill( const AddrInfo* addr, const char* const why );
|
void EnqueueKill( const AddrInfo* addr, const char* const why );
|
||||||
|
|
||||||
|
bool IsCurrent( const AddrInfo* addr );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef enum { Q_READ, Q_KILL } QAction;
|
typedef enum { Q_READ, Q_KILL } QAction;
|
||||||
typedef struct { QAction m_act; SockInfo m_info; } QueuePr;
|
typedef struct { QAction m_act; SockInfo m_info; } QueuePr;
|
||||||
|
|
||||||
/* Remove from set being listened on */
|
/* Remove from set being listened on */
|
||||||
bool RemoveSocket( const AddrInfo* addr );
|
bool RemoveSocket( const AddrInfo* addr );
|
||||||
|
/* test if is in set being listened on */
|
||||||
|
bool SocketFound( const AddrInfo* addr );
|
||||||
|
|
||||||
void enqueue( QAction act = Q_READ );
|
void enqueue( QAction act = Q_READ );
|
||||||
void enqueue( SockInfo si, QAction act = Q_READ );
|
void enqueue( SockInfo si, QAction act = Q_READ );
|
||||||
|
@ -92,7 +96,7 @@ class XWThreadPool {
|
||||||
static void* listener_main( void* closure );
|
static void* listener_main( void* closure );
|
||||||
|
|
||||||
/* Sockets main thread listens on */
|
/* Sockets main thread listens on */
|
||||||
vector<SockInfo>m_activeSockets;
|
map<int, SockInfo>m_activeSockets;
|
||||||
pthread_rwlock_t m_activeSocketsRWLock;
|
pthread_rwlock_t m_activeSocketsRWLock;
|
||||||
|
|
||||||
/* Sockets waiting for a thread to read 'em */
|
/* Sockets waiting for a thread to read 'em */
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
|
/* -*- compile-command: "make -j3"; -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 2013 by Eric House (xwords@eehouse.org). All rights reserved.
|
* Copyright 2013 by Eric House (xwords@eehouse.org). All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -17,6 +17,7 @@
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
#include "udpack.h"
|
#include "udpack.h"
|
||||||
#include "mlock.h"
|
#include "mlock.h"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* -*- compile-command: "make -k -j3"; -*- */
|
/* -*- compile-command: "make -k -j3"; -*- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2010-2012 by Eric House (xwords@eehouse.org). All rights
|
* Copyright 2010-2013 by Eric House (xwords@eehouse.org). All rights
|
||||||
* reserved.
|
* reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -19,6 +19,7 @@
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include "udpqueue.h"
|
#include "udpqueue.h"
|
||||||
#include "mlock.h"
|
#include "mlock.h"
|
||||||
|
|
||||||
|
@ -36,8 +37,43 @@ UdpThreadClosure::logStats()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PartialPacket::stillGood() const
|
||||||
|
{
|
||||||
|
return 0 == m_errno
|
||||||
|
|| EAGAIN == m_errno
|
||||||
|
|| EWOULDBLOCK == m_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PartialPacket::readAtMost( int len )
|
||||||
|
{
|
||||||
|
bool success = false;
|
||||||
|
uint8_t tmp[len];
|
||||||
|
ssize_t nRead = recv( m_sock, tmp, len, 0 );
|
||||||
|
if ( 0 > nRead ) { // error case
|
||||||
|
m_errno = errno;
|
||||||
|
if ( !stillGood() ) {
|
||||||
|
logf( XW_LOGERROR, "%s(len=%d, socket=%d): recv failed: %d (%s)", __func__,
|
||||||
|
len, m_sock, m_errno, strerror(m_errno) );
|
||||||
|
}
|
||||||
|
} else if ( 0 == nRead ) { // remote socket closed
|
||||||
|
logf( XW_LOGINFO, "%s: remote closed (socket=%d)", __func__, m_sock );
|
||||||
|
m_errno = -1; // so stillGood will fail
|
||||||
|
} else {
|
||||||
|
m_errno = 0;
|
||||||
|
success = len == nRead;
|
||||||
|
int curSize = m_buf.size();
|
||||||
|
m_buf.resize( nRead + curSize );
|
||||||
|
memcpy( &m_buf[curSize], tmp, nRead );
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
UdpQueue::UdpQueue()
|
UdpQueue::UdpQueue()
|
||||||
{
|
{
|
||||||
|
m_nextID = 0;
|
||||||
|
pthread_mutex_init ( &m_partialsMutex, NULL );
|
||||||
pthread_mutex_init ( &m_queueMutex, NULL );
|
pthread_mutex_init ( &m_queueMutex, NULL );
|
||||||
pthread_cond_init( &m_queueCondVar, NULL );
|
pthread_cond_init( &m_queueCondVar, NULL );
|
||||||
|
|
||||||
|
@ -52,6 +88,7 @@ UdpQueue::~UdpQueue()
|
||||||
{
|
{
|
||||||
pthread_cond_destroy( &m_queueCondVar );
|
pthread_cond_destroy( &m_queueCondVar );
|
||||||
pthread_mutex_destroy ( &m_queueMutex );
|
pthread_mutex_destroy ( &m_queueMutex );
|
||||||
|
pthread_mutex_destroy ( &m_partialsMutex );
|
||||||
}
|
}
|
||||||
|
|
||||||
UdpQueue*
|
UdpQueue*
|
||||||
|
@ -63,16 +100,91 @@ UdpQueue::get()
|
||||||
return s_instance;
|
return s_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return false if socket should no longer be used
|
||||||
|
bool
|
||||||
|
UdpQueue::handle( const AddrInfo* addr, QueueCallback cb )
|
||||||
|
{
|
||||||
|
PartialPacket* packet;
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
int sock = addr->socket();
|
||||||
|
|
||||||
|
// Hang onto this mutex for as long as we may be writing to the packet
|
||||||
|
// since having it deleted while in use would be bad.
|
||||||
|
MutexLock ml( &m_partialsMutex );
|
||||||
|
|
||||||
|
map<int, PartialPacket*>::iterator iter = m_partialPackets.find( sock );
|
||||||
|
if ( m_partialPackets.end() == iter ) {
|
||||||
|
packet = new PartialPacket( sock );
|
||||||
|
m_partialPackets.insert( pair<int, PartialPacket*>( sock, packet ) );
|
||||||
|
} else {
|
||||||
|
packet = iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First see if we've read the length bytes
|
||||||
|
if ( packet->readSoFar() < sizeof( packet->m_len ) ) {
|
||||||
|
if ( packet->readAtMost( sizeof(packet->m_len) - packet->readSoFar() ) ) {
|
||||||
|
uint16_t tmp;
|
||||||
|
memcpy( &tmp, packet->data(), sizeof(tmp) );
|
||||||
|
packet->m_len = ntohs(tmp);
|
||||||
|
success = 0 < packet->m_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( success && packet->readSoFar() >= sizeof( packet->m_len ) ) {
|
||||||
|
assert( 0 < packet->m_len );
|
||||||
|
int leftToRead =
|
||||||
|
packet->m_len - (packet->readSoFar() - sizeof(packet->m_len));
|
||||||
|
if ( packet->readAtMost( leftToRead ) ) {
|
||||||
|
handle( addr, packet->data() + sizeof(packet->m_len),
|
||||||
|
packet->m_len, cb );
|
||||||
|
packet = NULL;
|
||||||
|
newSocket_locked( sock );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
success = success && (NULL == packet || packet->stillGood());
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
UdpQueue::handle( const AddrInfo* addr, unsigned char* buf, int len,
|
UdpQueue::handle( const AddrInfo* addr, const uint8_t* buf, int len,
|
||||||
QueueCallback cb )
|
QueueCallback cb )
|
||||||
{
|
{
|
||||||
UdpThreadClosure* utc = new UdpThreadClosure( addr, buf, len, cb );
|
UdpThreadClosure* utc = new UdpThreadClosure( addr, buf, len, cb );
|
||||||
MutexLock ml( &m_queueMutex );
|
MutexLock ml( &m_queueMutex );
|
||||||
|
int id = ++m_nextID;
|
||||||
|
utc->setID( id );
|
||||||
|
logf( XW_LOGINFO, "%s: enqueuing packet %d (socket %d, len %d)",
|
||||||
|
__func__, id, addr->socket(), len );
|
||||||
m_queue.push_back( utc );
|
m_queue.push_back( utc );
|
||||||
|
|
||||||
pthread_cond_signal( &m_queueCondVar );
|
pthread_cond_signal( &m_queueCondVar );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UdpQueue::newSocket_locked( int sock )
|
||||||
|
{
|
||||||
|
map<int, PartialPacket*>::iterator iter = m_partialPackets.find( sock );
|
||||||
|
if ( m_partialPackets.end() != iter ) {
|
||||||
|
delete iter->second;
|
||||||
|
m_partialPackets.erase( iter );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void
|
||||||
|
UdpQueue::newSocket( int sock )
|
||||||
|
{
|
||||||
|
MutexLock ml( &m_partialsMutex );
|
||||||
|
newSocket_locked( sock );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UdpQueue::newSocket( const AddrInfo* addr )
|
||||||
|
{
|
||||||
|
assert( addr->isTCP() );
|
||||||
|
newSocket( addr->socket() );
|
||||||
|
}
|
||||||
|
|
||||||
void*
|
void*
|
||||||
UdpQueue::thread_main()
|
UdpQueue::thread_main()
|
||||||
{
|
{
|
||||||
|
@ -83,9 +195,12 @@ UdpQueue::thread_main()
|
||||||
}
|
}
|
||||||
UdpThreadClosure* utc = m_queue.front();
|
UdpThreadClosure* utc = m_queue.front();
|
||||||
m_queue.pop_front();
|
m_queue.pop_front();
|
||||||
|
|
||||||
pthread_mutex_unlock( &m_queueMutex );
|
pthread_mutex_unlock( &m_queueMutex );
|
||||||
|
|
||||||
utc->noteDequeued();
|
utc->noteDequeued();
|
||||||
|
logf( XW_LOGINFO, "%s: dispatching packet %d (socket %d)", __func__,
|
||||||
|
utc->getID(), utc->addr()->socket() );
|
||||||
(*utc->cb())( utc );
|
(*utc->cb())( utc );
|
||||||
utc->logStats();
|
utc->logStats();
|
||||||
delete utc;
|
delete utc;
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "xwrelay_priv.h"
|
#include "xwrelay_priv.h"
|
||||||
#include "addrinfo.h"
|
#include "addrinfo.h"
|
||||||
|
@ -35,9 +36,9 @@ typedef void (*QueueCallback)( UdpThreadClosure* closure );
|
||||||
|
|
||||||
class UdpThreadClosure {
|
class UdpThreadClosure {
|
||||||
public:
|
public:
|
||||||
UdpThreadClosure( const AddrInfo* addr, unsigned char* buf,
|
UdpThreadClosure( const AddrInfo* addr, const uint8_t* buf,
|
||||||
int len, QueueCallback cb )
|
int len, QueueCallback cb )
|
||||||
: m_buf(new unsigned char[len])
|
: m_buf(new uint8_t[len])
|
||||||
, m_len(len)
|
, m_len(len)
|
||||||
, m_addr(*addr)
|
, m_addr(*addr)
|
||||||
, m_cb(cb)
|
, m_cb(cb)
|
||||||
|
@ -46,7 +47,7 @@ public:
|
||||||
memcpy( m_buf, buf, len );
|
memcpy( m_buf, buf, len );
|
||||||
}
|
}
|
||||||
|
|
||||||
~UdpThreadClosure() { delete m_buf; }
|
~UdpThreadClosure() { delete[] m_buf; }
|
||||||
|
|
||||||
const unsigned char* buf() const { return m_buf; }
|
const unsigned char* buf() const { return m_buf; }
|
||||||
int len() const { return m_len; }
|
int len() const { return m_len; }
|
||||||
|
@ -55,14 +56,37 @@ public:
|
||||||
void noteDequeued() { m_dequed = time( NULL ); }
|
void noteDequeued() { m_dequed = time( NULL ); }
|
||||||
void logStats();
|
void logStats();
|
||||||
const QueueCallback cb() const { return m_cb; }
|
const QueueCallback cb() const { return m_cb; }
|
||||||
|
void setID( int id ) { m_id = id; }
|
||||||
|
int getID( void ) { return m_id; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned char* m_buf;
|
uint8_t* m_buf;
|
||||||
int m_len;
|
int m_len;
|
||||||
AddrInfo m_addr;
|
AddrInfo m_addr;
|
||||||
QueueCallback m_cb;
|
QueueCallback m_cb;
|
||||||
time_t m_created;
|
time_t m_created;
|
||||||
time_t m_dequed;
|
time_t m_dequed;
|
||||||
|
int m_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PartialPacket {
|
||||||
|
public:
|
||||||
|
PartialPacket(int sock)
|
||||||
|
:m_len(0)
|
||||||
|
,m_sock(sock)
|
||||||
|
,m_errno(0)
|
||||||
|
{}
|
||||||
|
bool stillGood() const ;
|
||||||
|
bool readAtMost( int len );
|
||||||
|
size_t readSoFar() const { return m_buf.size(); }
|
||||||
|
const uint8_t* data() const { return m_buf.data(); }
|
||||||
|
|
||||||
|
unsigned short m_len; /* decoded via ntohs from the first 2 bytes */
|
||||||
|
private:
|
||||||
|
|
||||||
|
vector<uint8_t> m_buf;
|
||||||
|
int m_sock;
|
||||||
|
int m_errno;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UdpQueue {
|
class UdpQueue {
|
||||||
|
@ -70,17 +94,24 @@ class UdpQueue {
|
||||||
static UdpQueue* get();
|
static UdpQueue* get();
|
||||||
UdpQueue();
|
UdpQueue();
|
||||||
~UdpQueue();
|
~UdpQueue();
|
||||||
void handle( const AddrInfo* addr, unsigned char* buf, int len,
|
bool handle( const AddrInfo* addr, QueueCallback cb );
|
||||||
|
void handle( const AddrInfo* addr, const uint8_t* buf, int len,
|
||||||
QueueCallback cb );
|
QueueCallback cb );
|
||||||
|
void newSocket( int socket );
|
||||||
|
void newSocket( const AddrInfo* addr );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void newSocket_locked( int sock );
|
||||||
static void* thread_main_static( void* closure );
|
static void* thread_main_static( void* closure );
|
||||||
void* thread_main();
|
void* thread_main();
|
||||||
|
|
||||||
|
pthread_mutex_t m_partialsMutex;
|
||||||
pthread_mutex_t m_queueMutex;
|
pthread_mutex_t m_queueMutex;
|
||||||
pthread_cond_t m_queueCondVar;
|
pthread_cond_t m_queueCondVar;
|
||||||
deque<UdpThreadClosure*> m_queue;
|
deque<UdpThreadClosure*> m_queue;
|
||||||
|
// map<int, vector<UdpThreadClosure*> > m_bySocket;
|
||||||
|
int m_nextID;
|
||||||
|
map<int, PartialPacket*> m_partialPackets;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,6 +31,8 @@ UDPPORT=10997
|
||||||
|
|
||||||
# default 5
|
# default 5
|
||||||
SOCK_TIMEOUT_SECONDS=5
|
SOCK_TIMEOUT_SECONDS=5
|
||||||
|
# How many tcp sockets at once (to prevent leaks). default: 100
|
||||||
|
MAXSOCKS=256
|
||||||
|
|
||||||
# And the control port is?
|
# And the control port is?
|
||||||
CTLPORT=11000
|
CTLPORT=11000
|
||||||
|
@ -54,6 +56,8 @@ SERVERNAME=eehouse.org
|
||||||
|
|
||||||
# name of the database. (Table names are hard-coded.)
|
# name of the database. (Table names are hard-coded.)
|
||||||
DB_NAME=xwgames
|
DB_NAME=xwgames
|
||||||
|
# UDP port postgres server is listening on
|
||||||
|
DB_PORT=5433
|
||||||
|
|
||||||
# Initial level of logging. See xwrelay_priv.h for values. Currently
|
# Initial level of logging. See xwrelay_priv.h for values. Currently
|
||||||
# 0 means errors only, 1 info, 2 verbose and 3 very verbose.
|
# 0 means errors only, 1 info, 2 verbose and 3 very verbose.
|
||||||
|
|
|
@ -203,16 +203,30 @@ parseRelayID( const unsigned char** const inp, const unsigned char* const end,
|
||||||
if ( ok ) {
|
if ( ok ) {
|
||||||
strncpy( buf, (char*)*inp, connNameLen );
|
strncpy( buf, (char*)*inp, connNameLen );
|
||||||
buf[connNameLen] = '\0';
|
buf[connNameLen] = '\0';
|
||||||
*hid = atoi( hidp+1 );
|
|
||||||
char* endptr;
|
++hidp; // skip '/'
|
||||||
*hid = strtol( hidp + 1, &endptr, 10 );
|
*hid = *hidp - '0'; // assume it's one byte, as should be in range '0'--'4'
|
||||||
if ( '\n' == *endptr ) {
|
// logf( XW_LOGERROR, "%s: read hid of %d from %s", __func__, *hid, hidp );
|
||||||
++endptr;
|
|
||||||
|
if ( *hid >= 0 && *hid <= 4 ) {
|
||||||
|
const char* endptr = hidp + 1;
|
||||||
|
if ( '\n' == *endptr ) {
|
||||||
|
++endptr;
|
||||||
|
}
|
||||||
|
*inp = (unsigned char*)endptr;
|
||||||
|
} else {
|
||||||
|
ok = false;
|
||||||
|
|
||||||
|
int len = end - *inp;
|
||||||
|
char buf[len+1];
|
||||||
|
memcpy( buf, *inp, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
logf( XW_LOGERROR, "%s: got bad hid %d from str \"%s\"", __func__,
|
||||||
|
*hid, buf );
|
||||||
}
|
}
|
||||||
*inp = (unsigned char*)endptr;
|
|
||||||
}
|
}
|
||||||
if ( !ok ) {
|
if ( !ok ) {
|
||||||
logf( XW_LOGERROR, "%s failed", __func__ );
|
logf( XW_LOGERROR, "%s failed", __func__ );
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
@ -448,23 +462,32 @@ send_with_length_unsafe( const AddrInfo* addr, const unsigned char* buf,
|
||||||
{
|
{
|
||||||
assert( !!addr );
|
assert( !!addr );
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
int socket = addr->socket();
|
|
||||||
|
|
||||||
if ( addr->isTCP() ) {
|
if ( addr->isTCP() ) {
|
||||||
unsigned short len = htons( bufLen );
|
int socket = addr->socket();
|
||||||
ssize_t nSent = send( socket, &len, 2, 0 );
|
if ( addr->isCurrent() ) {
|
||||||
if ( nSent == 2 ) {
|
unsigned short len = htons( bufLen );
|
||||||
nSent = send( socket, buf, bufLen, 0 );
|
ssize_t nSent = send( socket, &len, sizeof(len), 0 );
|
||||||
if ( nSent == ssize_t(bufLen) ) {
|
if ( nSent == sizeof(len) ) {
|
||||||
logf( XW_LOGINFO, "sent %d bytes on socket %d", nSent, socket );
|
nSent = send( socket, buf, bufLen, 0 );
|
||||||
ok = true;
|
if ( nSent == ssize_t(bufLen) ) {
|
||||||
|
logf( XW_LOGINFO, "sent %d bytes on socket %d", nSent, socket );
|
||||||
|
ok = true;
|
||||||
|
} else {
|
||||||
|
logf( XW_LOGERROR, "%s: send failed: %s (errno=%d)", __func__,
|
||||||
|
strerror(errno), errno );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
logf( XW_LOGINFO, "%s: dropping packet: socket %d reused",
|
||||||
|
__func__, socket );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AddrInfo::ClientToken clientToken = addr->clientToken();
|
AddrInfo::ClientToken clientToken = addr->clientToken();
|
||||||
assert( 0 != clientToken );
|
assert( 0 != clientToken );
|
||||||
clientToken = htonl(clientToken);
|
clientToken = htonl(clientToken);
|
||||||
const struct sockaddr* saddr = addr->sockaddr();
|
const struct sockaddr* saddr = addr->sockaddr();
|
||||||
|
int socket = addr->socket();
|
||||||
assert( g_udpsock == socket || socket == -1 );
|
assert( g_udpsock == socket || socket == -1 );
|
||||||
if ( -1 == socket ) {
|
if ( -1 == socket ) {
|
||||||
socket = g_udpsock;
|
socket = g_udpsock;
|
||||||
|
@ -600,7 +623,7 @@ processReconnect( const unsigned char* bufp, int bufLen, const AddrInfo* addr )
|
||||||
cookie, srcID, addr, clientVersion, &devID,
|
cookie, srcID, addr, clientVersion, &devID,
|
||||||
nPlayersH, nPlayersT, gameSeed, langCode,
|
nPlayersH, nPlayersT, gameSeed, langCode,
|
||||||
wantsPublic, makePublic );
|
wantsPublic, makePublic );
|
||||||
success = scr.Reconnect( srcID, nPlayersH, nPlayersT, gameSeed,
|
success = scr.Reconnect( nPlayersH, nPlayersT, gameSeed,
|
||||||
&err );
|
&err );
|
||||||
// if ( !success ) {
|
// if ( !success ) {
|
||||||
// assert( err != XWRELAY_ERROR_NONE );
|
// assert( err != XWRELAY_ERROR_NONE );
|
||||||
|
@ -695,14 +718,15 @@ forwardMessage( const unsigned char* buf, int buflen, const AddrInfo* addr )
|
||||||
|
|
||||||
if ( getNetShort( &bufp, end, &cookieID )
|
if ( getNetShort( &bufp, end, &cookieID )
|
||||||
&& getNetByte( &bufp, end, &src )
|
&& getNetByte( &bufp, end, &src )
|
||||||
&& getNetByte( &bufp, end, &dest ) ) {
|
&& getNetByte( &bufp, end, &dest )
|
||||||
logf( XW_LOGINFO, "cookieID = %d", cookieID );
|
&& 0 < src && 0 < dest ) {
|
||||||
|
|
||||||
if ( COOKIE_ID_NONE == cookieID ) {
|
if ( COOKIE_ID_NONE == cookieID ) {
|
||||||
SafeCref scr( addr );
|
SafeCref scr( addr );
|
||||||
success = scr.Forward( src, addr, dest, buf, buflen );
|
success = scr.Forward( src, addr, dest, buf, buflen );
|
||||||
} else {
|
} else {
|
||||||
SafeCref scr( cookieID ); /* won't work if not allcon; will be 0 */
|
/* won't work if not allcon; will be 0 */
|
||||||
|
SafeCref scr( cookieID, true );
|
||||||
success = scr.Forward( src, addr, dest, buf, buflen );
|
success = scr.Forward( src, addr, dest, buf, buflen );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -887,37 +911,6 @@ handlePipe( int sig )
|
||||||
logf( XW_LOGINFO, "%s", __func__ );
|
logf( XW_LOGINFO, "%s", __func__ );
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
read_packet( int sock, unsigned char* buf, int buflen )
|
|
||||||
{
|
|
||||||
int result = -1;
|
|
||||||
ssize_t nread;
|
|
||||||
unsigned short msgLen;
|
|
||||||
nread = recv( sock, &msgLen, sizeof(msgLen), MSG_WAITALL );
|
|
||||||
if ( 0 == nread ) {
|
|
||||||
logf( XW_LOGINFO, "%s: recv => 0: remote closed", __func__ );
|
|
||||||
} else if ( nread != sizeof(msgLen) ) {
|
|
||||||
logf( XW_LOGERROR, "%s: first recv => %d: %s", __func__,
|
|
||||||
nread, strerror(errno) );
|
|
||||||
} else {
|
|
||||||
msgLen = ntohs( msgLen );
|
|
||||||
if ( msgLen >= buflen ) {
|
|
||||||
logf( XW_LOGERROR, "%s: buf too small; need %d but have %d",
|
|
||||||
__func__, msgLen, buflen );
|
|
||||||
} else {
|
|
||||||
nread = recv( sock, buf, msgLen, MSG_WAITALL );
|
|
||||||
if ( nread == msgLen ) {
|
|
||||||
result = nread;
|
|
||||||
} else {
|
|
||||||
logf( XW_LOGERROR, "%s: second recv failed: %s", __func__,
|
|
||||||
strerror(errno) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} /* read_packet */
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pushShort( vector<unsigned char>& out, unsigned short num )
|
pushShort( vector<unsigned char>& out, unsigned short num )
|
||||||
{
|
{
|
||||||
|
@ -1110,9 +1103,9 @@ handleProxyMsgs( int sock, const AddrInfo* addr, const unsigned char* bufp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( end - bufp != 1 ) {
|
if ( end - bufp != 1 ) {
|
||||||
logf( XW_LOGERROR, "%s: buf != end: %p vs %p", __func__, bufp, end );
|
logf( XW_LOGERROR, "%s: buf != end: %p vs %p (+1)", __func__, bufp, end );
|
||||||
}
|
}
|
||||||
// assert( bufp == end ); // don't ship with this!!!
|
// assert( bufp == end ); // don't ship with this!!!
|
||||||
}
|
}
|
||||||
} // handleProxyMsgs
|
} // handleProxyMsgs
|
||||||
|
@ -1427,9 +1420,9 @@ udp_thread_proc( UdpThreadClosure* utc )
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_udp_packet( int udpsock )
|
read_udp_packet( int udpsock )
|
||||||
{
|
{
|
||||||
unsigned char buf[MAX_MSG_LEN];
|
uint8_t buf[MAX_MSG_LEN];
|
||||||
AddrInfo::AddrUnion saddr;
|
AddrInfo::AddrUnion saddr;
|
||||||
memset( &saddr, 0, sizeof(saddr) );
|
memset( &saddr, 0, sizeof(saddr) );
|
||||||
socklen_t fromlen = sizeof(saddr.addr_in);
|
socklen_t fromlen = sizeof(saddr.addr_in);
|
||||||
|
@ -1449,17 +1442,17 @@ void
|
||||||
string_printf( string& str, const char* fmt, ... )
|
string_printf( string& str, const char* fmt, ... )
|
||||||
{
|
{
|
||||||
const int origsiz = str.size();
|
const int origsiz = str.size();
|
||||||
int newsiz = 100;
|
int addsiz = 100;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
for ( ; ; ) {
|
for ( ; ; ) {
|
||||||
str.resize( origsiz + newsiz );
|
str.resize( origsiz + addsiz );
|
||||||
|
|
||||||
va_start( ap, fmt );
|
va_start( ap, fmt );
|
||||||
int len = vsnprintf( (char *)str.c_str() + origsiz, newsiz, fmt, ap );
|
int len = vsnprintf( (char *)str.c_str() + origsiz, addsiz, fmt, ap );
|
||||||
va_end( ap );
|
va_end( ap );
|
||||||
|
|
||||||
if ( len > newsiz ) { // needs more space
|
if ( len >= addsiz ) { // needs more space
|
||||||
newsiz = len + 1;
|
addsiz = len + 1;
|
||||||
} else if ( -1 == len ) {
|
} else if ( -1 == len ) {
|
||||||
assert(0); // should be impossible
|
assert(0); // should be impossible
|
||||||
} else {
|
} else {
|
||||||
|
@ -1469,6 +1462,8 @@ string_printf( string& str, const char* fmt, ... )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Going with non-blocking instead
|
||||||
|
#if 0
|
||||||
static void
|
static void
|
||||||
set_timeouts( int sock )
|
set_timeouts( int sock )
|
||||||
{
|
{
|
||||||
|
@ -1493,6 +1488,7 @@ set_timeouts( int sock )
|
||||||
assert( 0 );
|
assert( 0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
enable_keepalive( int sock )
|
enable_keepalive( int sock )
|
||||||
|
@ -1500,7 +1496,7 @@ enable_keepalive( int sock )
|
||||||
int optval = 1;
|
int optval = 1;
|
||||||
if ( 0 > setsockopt( sock, SOL_SOCKET, SO_KEEPALIVE,
|
if ( 0 > setsockopt( sock, SOL_SOCKET, SO_KEEPALIVE,
|
||||||
&optval, sizeof( optval ) ) ) {
|
&optval, sizeof( optval ) ) ) {
|
||||||
logf( XW_LOGERROR, "setsockopt(SO_KEEPALIVE)=>%d (%s)", errno,
|
logf( XW_LOGERROR, "setsockopt(sock=%d, SO_KEEPALIVE)=>%d (%s)", sock, errno,
|
||||||
strerror(errno) );
|
strerror(errno) );
|
||||||
assert( 0 );
|
assert( 0 );
|
||||||
}
|
}
|
||||||
|
@ -1918,15 +1914,17 @@ main( int argc, char** argv )
|
||||||
errno, strerror(errno) );
|
errno, strerror(errno) );
|
||||||
assert( 0 ); // we're leaking files or load has grown
|
assert( 0 ); // we're leaking files or load has grown
|
||||||
} else {
|
} else {
|
||||||
// I've seen a bug where we accept but never service
|
// I've seen a bug where we accept but never service
|
||||||
// connections. Sockets are not closed, and so the
|
// connections. Sockets are not closed, and so the
|
||||||
// number goes up. Probably need a watchdog instead,
|
// number goes up. Probably need a watchdog instead,
|
||||||
// but this will work around it.
|
// but this will work around it.
|
||||||
assert( g_maxsocks > newSock );
|
assert( g_maxsocks > newSock );
|
||||||
|
|
||||||
/* Set timeout so send and recv won't block forever */
|
/* Set timeout so send and recv won't block forever */
|
||||||
set_timeouts( newSock );
|
// set_timeouts( newSock );
|
||||||
|
|
||||||
|
int err = fcntl( newSock, F_SETFL, O_NONBLOCK );
|
||||||
|
assert( 0 == err );
|
||||||
enable_keepalive( newSock );
|
enable_keepalive( newSock );
|
||||||
|
|
||||||
logf( XW_LOGINFO,
|
logf( XW_LOGINFO,
|
||||||
|
@ -1939,6 +1937,7 @@ main( int argc, char** argv )
|
||||||
perGame ? game_thread_proc
|
perGame ? game_thread_proc
|
||||||
: proxy_thread_proc,
|
: proxy_thread_proc,
|
||||||
&addr );
|
&addr );
|
||||||
|
UdpQueue::get()->newSocket( &addr );
|
||||||
}
|
}
|
||||||
--retval;
|
--retval;
|
||||||
}
|
}
|
||||||
|
@ -1948,10 +1947,10 @@ main( int argc, char** argv )
|
||||||
// run_ctrl_thread( g_control );
|
// run_ctrl_thread( g_control );
|
||||||
--retval;
|
--retval;
|
||||||
}
|
}
|
||||||
if ( FD_ISSET( g_udpsock, &rfds ) ) {
|
if ( -1 != g_udpsock && FD_ISSET( g_udpsock, &rfds ) ) {
|
||||||
// This will need to be done in a separate thread, or pushed
|
// This will need to be done in a separate thread, or pushed
|
||||||
// to the existing thread pool
|
// to the existing thread pool
|
||||||
handle_udp_packet( g_udpsock );
|
read_udp_packet( g_udpsock );
|
||||||
--retval;
|
--retval;
|
||||||
}
|
}
|
||||||
#ifdef DO_HTTP
|
#ifdef DO_HTTP
|
||||||
|
|
|
@ -53,7 +53,7 @@ cid integer
|
||||||
,nPerDevice INTEGER[]
|
,nPerDevice INTEGER[]
|
||||||
,seeds INTEGER[]
|
,seeds INTEGER[]
|
||||||
,ack VARCHAR(1)[]
|
,ack VARCHAR(1)[]
|
||||||
,nSent INTEGER DEFAULT 0
|
,nsents INTEGER[] DEFAULT '{0,0,0,0}'
|
||||||
,ctime TIMESTAMP (0) DEFAULT CURRENT_TIMESTAMP
|
,ctime TIMESTAMP (0) DEFAULT CURRENT_TIMESTAMP
|
||||||
,mtimes TIMESTAMP(0)[]
|
,mtimes TIMESTAMP(0)[]
|
||||||
,addrs INET[]
|
,addrs INET[]
|
||||||
|
|
Loading…
Reference in a new issue