Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

752 lines
15 KiB

#include <process.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <rpc.h>
#include "poker.h"
#include "pokersrv.h"
#include "osutil_s.h"
#include "pokrpc.h"
// BUGBUG - These are defined in POKERSRV.C and should be moved to a header file
extern EVENT *player_work_to_do;
extern EVENT new_player_event;
// Global Data for dispatching callbacks to worker threads
// Ids of client callback functions
enum
{
HEARTBEAT,
DISPLAY_HANDS,
GET_BET,
DISPLAY_BET,
PROMPT_FOR_DRAW,
DISPLAY_CARDS_DRAWN,
DISPLAY_WINNER,
DISPLAY_MONEY_TOTALS,
DISPLAY_PLAYERS,
DISPLAY_PLAYER_CHANGE,
DISPLAY_DEALER_NAME,
PLAY_ANOTHER_HAND,
CONTINUE_WAITING,
DISPLAY_WAITING_MESSAGE,
CHOOSE_GAME,
DISPLAY_GAME,
} client_function_id;
// Per-function data used by callback functions
union
{
struct
{
MY_BOOL response;
} hb_data;
struct
{
short first_up_card;
short last_up_card;
} dh_data;
struct
{
BETTING_OPTIONS *options;
MONEY *amount;
} gb_data;
struct
{
char *name;
BETTING_OPTIONS options;
MONEY amount;
} pb_data;
struct
{
HAND *hand;
unsigned short mask;
} pfd_data;
struct
{
char *name;
unsigned short num_cards;
} pcd_data;
struct
{
PLAYER * *winners;
short count;
POKER_HAND_WEIGHT weight;
short players_in;
MONEY pot;
} dw_data;
// struct {} pmt_data;
// struct {} pip_data;
struct
{
unsigned char *name;
MY_BOOL joining;
} ppc_data;
struct
{
unsigned char *name;
} pdn_data;
struct
{
MY_BOOL response;
} pah_data;
struct
{
MY_BOOL response;
} cw_data;
// struct {} pwm_data;
struct
{
unsigned short index;
} cg_data;
struct
{
char *name;
unsigned short index;
} pg_data;
} per_function_data;
// Counter of threads currently performing callback to client, with
// mutex for protection.
STATIC short client_counter;
STATIC MUTEX client_counter_mutex;
// Event used to notify main thread when all workers are done performing
// the callback to the client.
STATIC EVENT clients_done;
// Flag that indicates whether an exception has occurred on a callback
// Note: This is only used in the one-client case, and relies on the
// fact that if the client disconnects, the exception won't occur until
//the server actually makes the callback.
MY_BOOL callback_exception;
// DispatchInit - Initializes global data used by the functions in this file
MY_BOOL
DispatchInit(void)
{
if (! MutexInit(&client_counter_mutex))
return FALSE_B;
if (! EventInit(&clients_done))
return FALSE_B;
return TRUE_B;
}
// Server versions of client callback functions
//
// Because the control flow of the server is single-threaded but the
// callbacks to the clients must be done in the context of the clients'
// call threads, it is necessary to make calls to the clients via indirect
// dispatching. The following functions do the dispatching of individual
// calls by packaging up the data and waking up the appropriate client
// thread(s), which is (are) blocked in the WaitForInstructions() call.
// Helper functions
STATIC void
CallAllClients(void)
{
short s;
// Set the "wait for clients" flag
EventSet(&clients_done);
// Lock access to the number of clients who need to make this call
MutexLock(&client_counter_mutex, INFINITE);
client_counter = 0;
// Wake up the clients
for (s = 0; s < player_array_size; s++)
{
if (player_array[s].status == PLAYING)
{
EventClear(&player_work_to_do[s]);
client_counter++;
}
}
// Release the counter
MutexUnlock(&client_counter_mutex);
// Wait for the clients to complete
if (client_counter)
EventWaitForClear(&clients_done, INFINITE);
return;
}
STATIC void
CallAllButOneClient(int index)
{
int i;
// Set the "wait for clients" flag
EventSet(&clients_done);
// Lock access to the number of clients who need to make this call
MutexLock(&client_counter_mutex, INFINITE);
client_counter = 0;
// Wake up the clients
for (i = 0; i < (int) player_array_size; i++)
{
if (player_array[i].status == PLAYING && i != index)
{
EventClear(&player_work_to_do[i]);
client_counter++;
}
}
// Release the counter
MutexUnlock(&client_counter_mutex);
// Wait for the clients to complete
if (client_counter)
EventWaitForClear(&clients_done, INFINITE);
return;
}
STATIC MY_BOOL
CallOneClient(int index)
{
// Set the "wait for clients" flag
EventSet(&clients_done);
// Clear the exception flag
callback_exception = FALSE_B;
// Set the number of clients who need to make this call
MutexLock(&client_counter_mutex, INFINITE);
client_counter = 1;
MutexUnlock(&client_counter_mutex);
// Wake up the client
EventClear(&player_work_to_do[index]);
// Wait for the clients to complete
EventWaitForClear(&clients_done, INFINITE);
return callback_exception;
}
// This function is called by DeletePlayer(). It causes
// WaitForInstructions() to awaken and return to the client.
void ReturnControlToClient(PLAYER *player)
{
// This function assumes that <active> has been set to false already
ASSERT(player->status == NOT_PLAYING);
// Wake up the client's thread (which causes WaitForInstructions
// to return)
EventClear(&player_work_to_do[player - player_array]);
}
// Server-side dispatchers for client functions
MY_BOOL
Heartbeat(PLAYER *player, MY_BOOL b_default)
{
// Set up the per-function data
client_function_id = HEARTBEAT;
// Dispatch the call
if (CallOneClient(player - player_array))
{
// An exception occurred; use the default value
return b_default;
}
else
return per_function_data.hb_data.response;
}
void
DisplayHands(short first_up_card, short last_up_card)
{
// Set up the per-function data
client_function_id = DISPLAY_HANDS;
per_function_data.dh_data.first_up_card = first_up_card;
per_function_data.dh_data.last_up_card = last_up_card;
// Dispatch the call
CallAllClients();
}
void
GetBet(PLAYER *player,
BETTING_OPTIONS *options,
MONEY *amount,
BETTING_OPTIONS bo_default,
MONEY m_default)
{
// Set up the per-function data
client_function_id = GET_BET;
per_function_data.gb_data.options = options;
per_function_data.gb_data.amount = amount;
// Dispatch the call
if (CallOneClient(player - player_array))
{
// An exception occurred; use the default values
*options = bo_default;
*amount = m_default;
}
return;
}
void
DisplayBet(PLAYER *player, char *name, BETTING_OPTIONS options, MONEY amount)
{
// Set up the per-function data
client_function_id = DISPLAY_BET;
per_function_data.pb_data.name = name;
per_function_data.pb_data.options = options;
per_function_data.pb_data.amount = amount;
// Dispatch the call
CallAllButOneClient(player - player_array);
return;
}
unsigned short
PromptForDraw(PLAYER *player, HAND *hand, unsigned short us_default)
{
// Set up the per-function data
client_function_id = PROMPT_FOR_DRAW;
per_function_data.pfd_data.hand = hand;
// Dispatch the call
if (CallOneClient(player - player_array))
{
// An exception occurred; use the default value
return us_default;
}
else
return per_function_data.pfd_data.mask;
}
void
DisplayCardsDrawn(PLAYER *player, char *name, unsigned short num_cards)
{
// Set up the per-function data
client_function_id = DISPLAY_CARDS_DRAWN;
per_function_data.pcd_data.name = name;
per_function_data.pcd_data.num_cards = num_cards;
// Dispatch the call
CallAllButOneClient(player - player_array);
return;
}
void
DisplayWinner(PLAYER * *winners,
short count,
POKER_HAND_WEIGHT weight,
short players_in,
MONEY pot)
{
// Set up the per-function data
client_function_id = DISPLAY_WINNER;
per_function_data.dw_data.winners = winners;
per_function_data.dw_data.count = count;
per_function_data.dw_data.weight = weight;
per_function_data.dw_data.players_in = players_in;
per_function_data.dw_data.pot = pot;
// Dispatch the call
CallAllClients();
return;
}
void
DisplayMoneyTotals(void)
{
// Set up the per-function data
client_function_id = DISPLAY_MONEY_TOTALS;
// Dispatch the call
CallAllClients();
return;
}
void
DisplayPlayers(PLAYER *player)
{
// Set up the per-function data
client_function_id = DISPLAY_PLAYERS;
// Dispatch the call
if (player != NULL)
CallOneClient(player - player_array);
else
CallAllClients();
return;
}
void
DisplayPlayerChange(char *name, MY_BOOL joining)
{
// Set up the per-function data
client_function_id = DISPLAY_PLAYER_CHANGE;
per_function_data.ppc_data.name = name;
per_function_data.ppc_data.joining = joining;
// Dispatch the call
CallAllClients();
return;
}
void
DisplayDealerName(char *name)
{
// Set up the per-function data
client_function_id = DISPLAY_DEALER_NAME;
per_function_data.pdn_data.name = name;
// Dispatch the call
CallAllClients();
return;
}
MY_BOOL
PlayAnotherHand(PLAYER *player, MY_BOOL b_default)
{
// Set up the per-function data
client_function_id = PLAY_ANOTHER_HAND;
// Dispatch the call
if (CallOneClient(player - player_array))
{
// An exception occurred; use the default value
return b_default;
}
else
return per_function_data.pah_data.response;
}
MY_BOOL
ContinueWaiting(PLAYER *player, MY_BOOL b_default)
{
// Set up the per-function data
client_function_id = CONTINUE_WAITING;
// Dispatch the call
if (CallOneClient(player - player_array))
{
// An exception occurred; use the default value
return b_default;
}
else
return per_function_data.cw_data.response;
}
void DisplayWaitingMessage(void)
{
// Set up the per-function data
client_function_id = DISPLAY_WAITING_MESSAGE;
// Dispatch the call
CallAllClients();
return;
}
unsigned short
ChooseGame(PLAYER *dealer, unsigned short us_default)
{
// Set up the per-function data
client_function_id = CHOOSE_GAME;
// Dispatch the call
if (CallOneClient(dealer - player_array))
{
// An exception occurred; use the default value
return us_default;
}
else
return per_function_data.cg_data.index;
}
void
DisplayGame(PLAYER *dealer, unsigned short index)
{
// Set up the per-function data
client_function_id = DISPLAY_GAME;
per_function_data.pg_data.name = dealer->name;
per_function_data.pg_data.index = index;
// Dispatch the call
CallAllButOneClient(dealer - player_array);
return;
}
// This is an RPC'd function that a client calls to participate in the game
void
Server_WaitForInstructions(short index)
{
PLAYER *player = &player_array[index];
ASSERT(index >= 0 && index < player_array_size);
// If the player gets here, we set the status from join-request to
// join-pending, and indicate that a new player has connected.
ASSERT(player->status == JOIN_REQUEST);
player->status = JOIN_PENDING;
EventClear(&new_player_event);
// Loop until the player quits
while (1)
{
// Wait until the table manager indicates that there's something for
// this player to do
EventWaitForClear(&player_work_to_do[index], INFINITE);
// Set the event again, for next time
EventSet(&player_work_to_do[index]);
// Break out of the loop here if the player is gone
if (player->status == NOT_PLAYING)
break;
// Perform the appropriate operation (which involves a callback to
// the client)
RpcTryExcept
{
// Raise an exception if the client is already leave-pending
if (player->status == LEAVE_PENDING)
// BUGBUG - Use a real exception #
RpcRaiseException(1);
// The heartbeat function is the only one that should be
// called on a JOIN_PENDING player
ASSERT(player->status == PLAYING ||
(player->status == JOIN_PENDING &&
client_function_id == HEARTBEAT) );
switch(client_function_id)
{
case HEARTBEAT:
per_function_data.hb_data.response =
Client_Heartbeat();
break;
case DISPLAY_HANDS:
Client_DisplayHands(player_array,
player_array_size,
index,
per_function_data.dh_data.first_up_card,
per_function_data.dh_data.last_up_card);
break;
case GET_BET:
Client_GetBet(per_function_data.gb_data.options,
per_function_data.gb_data.amount);
break;
case DISPLAY_BET:
Client_DisplayBet(per_function_data.pb_data.name,
per_function_data.pb_data.options,
per_function_data.pb_data.amount);
break;
case PROMPT_FOR_DRAW:
per_function_data.pfd_data.mask
= Client_PromptForDraw(per_function_data.pfd_data.hand);
break;
case DISPLAY_CARDS_DRAWN:
Client_DisplayCardsDrawn(per_function_data.pcd_data.name,
per_function_data.pcd_data.num_cards);
break;
case DISPLAY_WINNER:
Client_DisplayWinner(per_function_data.dw_data.winners,
per_function_data.dw_data.count,
per_function_data.dw_data.weight,
per_function_data.dw_data.players_in,
per_function_data.dw_data.pot);
break;
case DISPLAY_MONEY_TOTALS:
Client_DisplayMoneyTotals(player_array,
player_array_size);
break;
case DISPLAY_PLAYERS:
Client_DisplayPlayers(player_array,
player_array_size);
break;
case DISPLAY_PLAYER_CHANGE:
Client_DisplayPlayerChange(per_function_data.ppc_data.name,
per_function_data.ppc_data.joining);
break;
case DISPLAY_DEALER_NAME:
Client_DisplayDealerName(per_function_data.pdn_data.name);
break;
case PLAY_ANOTHER_HAND:
per_function_data.pah_data.response =
Client_PlayAnotherHand();
break;
case CONTINUE_WAITING:
per_function_data.cw_data.response =
Client_ContinueWaiting();
break;
case DISPLAY_WAITING_MESSAGE:
Client_DisplayWaitingMessage();
break;
case CHOOSE_GAME:
per_function_data.cg_data.index =
Client_ChooseGame(player_count);
break;
case DISPLAY_GAME:
Client_DisplayGame(per_function_data.pg_data.name,
per_function_data.pg_data.index);
break;
default:
ASSERT(0);
break;
}
}
RpcExcept(RpcExceptionCode())
{
#ifdef DEBUG
fprintf(stderr,
"Exception number %lu occurred, player=%s, index=%hd.\n",
(unsigned long) RpcExceptionCode(),
player->name,
index);
#endif
// Set the exception flag (useful only in the one-client case)
// and mark the player as LEAVE_PENDING
callback_exception = TRUE_B;
if (player->status == PLAYING)
player->status = LEAVE_PENDING;
}
RpcEndExcept
// We're done with the callback; decrement the client counter,
// and if we're the last client to finish, indicate this
MutexLock(&client_counter_mutex, INFINITE);
if (--client_counter == 0)
{
EventClear(&clients_done);
}
MutexUnlock(&client_counter_mutex);
} // End of <while> loop
return;
}