- 在线时间
- 1957 小时
- 最后登录
- 2024-6-29
- 注册时间
- 2004-4-26
- 听众数
- 49
- 收听数
- 0
- 能力
- 60 分
- 体力
- 40957 点
- 威望
- 6 点
- 阅读权限
- 255
- 积分
- 23862
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 20501
- 主题
- 18182
- 精华
- 5
- 分享
- 0
- 好友
- 140
TA的每日心情 | 奋斗 2024-6-23 05:14 |
|---|
签到天数: 1043 天 [LV.10]以坛为家III
 群组: 万里江山 群组: sas讨论小组 群组: 长盛证券理财有限公司 群组: C 语言讨论组 群组: Matlab讨论组 |
<DIV class=HtmlCode>1 @' `4 g O/ n" N/ q) I
< >// File: mazeserver.cpp
2 ^- T( Y- N! L+ f% @5 O, o//
% T- A4 F* j/ ^$ U* T// Desc: see main.cpp6 P, a+ ?# }# ~" C4 C* `
//
1 p% h" l; z1 D' N8 K' J// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
# ^( D0 @4 b; f//-----------------------------------------------------------------------------
) {& v/ N4 I& m: G( ^1 f#define STRICT" k$ T: u" P% v5 b" i/ U& x9 B
#define D3D_OVERLOADS
% W4 U% D- a7 G* w9 [1 s#include <windows.h>+ H( Y7 ]8 A: W/ d2 }
#include <d3dx.h>7 T9 r& N2 @% v1 k9 p
#include <stdio.h>7 s, P, N# D* D N
#include <math.h>- x* R7 d+ m* g; a$ H
#include <mmsystem.h>8 e9 l! G2 ~+ A9 X" U
#include <dplay8.h>
! Q8 A$ T0 P7 o5 f9 Z; c& [2 Q#include <dpaddr.h>- {3 R# s* y: m8 i" v' X+ o) K
#include <dxerr8.h>$ I! S, G: g6 _- V- E
#include "DXUtil.h"( |! M c( L8 l" U% n0 e2 I1 E
#include "MazeServer.h" h1 _- A0 ]: f/ T: o' s( f# i
#include " ackets.h"
/ N7 U M% ]' t0 L- f#include "Maze.h"9 w- Y# ~2 _ v
#include <malloc.h>+ T8 }. O, Y2 a
#include <tchar.h></P>* y' j9 V& M, E7 G: a& p7 a
0 [. M T4 f/ m/ U* ^% c< >//-----------------------------------------------------------------------------) r; R9 Q' Q* c( |5 W
// Name:
6 N3 |! F; X* R7 j" n4 I( W3 r! I// Desc:
6 q- C% {& J. I3 _" ?1 t//-----------------------------------------------------------------------------( e( w9 x: ]+ v/ b7 G! g
CMazeServer::CMazeServer()
0 ?* ~8 s% _* L4 f/ |5 U{/ W; \0 t& j. z! z! r5 F/ @2 G
m_dwPlayerCount = 0;
S& E* m- ]9 F& ~, b/ a+ E- V Q$ v0 K8 u9 W
m_wActiveThreadCount = 0;
& W( H/ c0 B1 q) M m_wMaxThreadCount = 0;
S" S2 b* Q: p$ ~# `/ G6 }! B* d" K$ d m_fAvgThreadCount = 0; b1 W: \, {1 H* h* u
m_fAvgThreadTime = 0;/ J4 f: Z! L* A
m_fMaxThreadTime = 0;</P>+ ?/ e' h. G% Q4 [% T- n
< > m_dwServerReliableRate = 15;
4 o) p. R7 Y. A' x. L m_dwServerTimeout = 150;% M6 ?/ @' v/ a9 A3 d
m_dwLogLevel = 2;
5 m! r$ L; H, H: l+ R9 Q) Z m_pMaze = NULL;</P>
% U. R+ E9 Y, B& N< > m_ClientNetConfig.ubReliableRate = 15;3 {* X- E- F6 @1 w/ O |9 c* A
m_ClientNetConfig.wUpdateRate = 150;
( P. Z- }* |& u2 d W- } m_ClientNetConfig.wTimeout = 150;</P>; a4 e: f8 F8 m4 b, V# n1 t5 k o" x
< > m_ClientNetConfig.dwThreadWait = 0;</P>
4 @# N+ N/ g/ }- d% @7 ?< > m_ClientNetConfig.ubClientPackIndex = 0;* L4 d. x! C8 c
m_ClientNetConfig.ubServerPackIndex = 0;
1 f9 _2 p5 c% ~& x. i6 k; C for(WORD x = 0; x < PACK_ARRAY_SIZE; x++)
+ `9 M7 z$ P$ E {7 b) s1 m6 T+ j$ l
m_ClientNetConfig.wClientPackSizeArray[x] = 0;
. H1 F8 m! e( }2 ]8 }6 ^# g4 H m_ClientNetConfig.wServerPackSizeArray[x] = 0;+ _9 O7 |1 d* D9 \6 D+ y/ t" M
}
x0 l7 @% K4 m' t. P. H6 b}</P>. U, {* I$ Y. \' R7 z& a8 c3 z( a
5 t: i' b, B. f. t4 P< >
' K/ ^- D+ i: Y$ r5 u4 D e. x& U//-----------------------------------------------------------------------------
, O0 n* w& H1 p1 ]: R' \3 f) U// Name: ' |3 t% k- t9 o& T' G+ F) j2 g
// Desc: / E( m5 i" R1 N/ X
//-----------------------------------------------------------------------------
9 X# H7 d; A, p0 c0 M/ IHRESULT CMazeServer::Init( BOOL bLocalLoopback, const CMaze* pMaze )/ r. i' f* Y5 i3 W% \+ x
{
7 K2 T% }3 \$ s5 |* H m_bLocalLoopback = bLocalLoopback;
6 |1 k8 Q6 v6 R m_pMaze = pMaze;' d1 N- o7 i0 B* ~6 | n
if( m_pMaze == NULL )& p4 F( t$ {2 v% F. J8 o. a* X
return DXTRACE_ERR( TEXT(" aram"), E_FAIL );</P>
# Q A! ~8 }/ O% G+ p" k3 M5 ^* j/ [< > // Grab height and width of maze& x* Q1 w. ^/ W- x7 ?
m_dwWidth = m_pMaze->GetWidth();( s( N5 M; u! _' {1 x
m_dwHeight = m_pMaze->GetHeight();</P>( p0 R$ j* O, S9 Y
< > m_ClientNetConfig.dwMazeWidth = m_dwWidth;
' V7 W/ E- k, O: e' Z& Z m_ClientNetConfig.dwMazeHeight = m_dwHeight;</P>
" N, |7 a7 k) j' Q7 X8 Y. C< > // Validate size. Must be a power-of-2 times LOCK_GRID_SIZE. Compute the shifts.
6 J7 b+ A# ^# o- n+ T* o }7 ]- ^8 S. V if( m_dwWidth > SERVER_MAX_WIDTH || m_dwHeight > SERVER_MAX_HEIGHT )( k" J7 p' P! v
return DXTRACE_ERR( TEXT("Maze height and width need to be less than 128"), E_INVALIDARG );
0 _! V, \ r- ? if( (m_dwWidth % LOCK_GRID_SIZE) != 0 || (m_dwHeight % LOCK_GRID_SIZE) != 0 )- ]( e* r! `* {
return DXTRACE_ERR( TEXT("Maze height and width need to be divisable by 16"), E_INVALIDARG );</P>! v* p5 i, ?! }4 p7 S, F( U/ M
< > DWORD scale = m_dwWidth / LOCK_GRID_SIZE;) v/ Z+ r0 x* E9 ~
m_dwMazeXShift = 0;
) Q* r3 N" Y% b: \ while ( (scale >>= 1) )
( c0 F u/ o- k6 w m_dwMazeXShift++;</P>' v7 i6 s4 }1 H# b+ B* A
< > scale = m_dwHeight / LOCK_GRID_SIZE;$ n3 v! z) q* u/ x; D& s
m_dwMazeYShift = 0;: f* \0 y) y4 t8 u/ k/ c
while ( (scale >>= 1) )
: Z) j% r4 P- }" K1 K9 c m_dwMazeYShift++;</P>$ l. y$ R9 i, C5 e* d
< > if( ((DWORD(LOCK_GRID_SIZE) << m_dwMazeXShift) != m_dwWidth) ||
- f1 h" G5 p0 X$ ^& t8 | ((DWORD(LOCK_GRID_SIZE) << m_dwMazeYShift) != m_dwHeight) )0 U1 I7 d/ o. r' _' [1 Z
return DXTRACE_ERR( TEXT("Maze height and width need to be power of 2"), E_INVALIDARG );</P>" `. Q7 @/ v/ _, C
< > // Initialise the player list
3 b* O0 h' u* F2 M ZeroMemory( m_PlayerDatas, sizeof(m_PlayerDatas) );
4 c( k' B1 a" d m_pFirstActivePlayerData = NULL;
. `6 b& z( n6 s9 O m_pFirstFreePlayerData = m_PlayerDatas;
5 v4 U) I' y/ w' u2 m9 z for( DWORD i = 1; i < MAX_PLAYER_OBJECTS-1; i++ )
/ F4 r9 z' ?: s1 L' K {; H* r) {4 w; }& g$ N
m_PlayerDatas.pNext = &m_PlayerDatas[i+1];, g+ X/ M4 z( U2 }) S" {+ ?/ T
m_PlayerDatas.pPrevious = &m_PlayerDatas[i-1];, ?% q9 Z3 k% g# L" \# c
}</P>; W) D ~2 O1 R0 x c: Q
< > m_PlayerDatas[0].pNext = &m_PlayerDatas[1];- P. x1 L+ M0 b( T0 ?, s
m_PlayerDatas[MAX_PLAYER_OBJECTS-1].pPrevious = &m_PlayerDatas[MAX_PLAYER_OBJECTS-2];
/ V4 G. r; k- i$ [! _& A X: f m_dwActivePlayerDataCount = 0;
( E' v J2 J4 V, r8 V4 U: B& H m_dwPlayerDataUniqueValue = 0;</P>
0 I0 ~" C. K3 R1 u( c; B1 T< > // Initialise the cells
$ A2 `9 o! q4 z' J# _# C; e ZeroMemory( m_Cells, sizeof(m_Cells) );
1 n1 b) u! \+ k3 P3 X7 z1 k. _ ZeroMemory( &m_OffMapCell, sizeof(m_OffMapCell) );</P>
: l5 ]. }* ]0 @ L< > return S_OK;$ p+ F6 L- _+ p9 o' [
}</P>
/ g, W0 \3 h3 \2 r/ Q5 r9 S
8 J7 ^* p& P, C% R< >
! i' V' w' l v/ W- t//-----------------------------------------------------------------------------
% @8 c5 y( @& T% K// Name:
3 [ T" ~4 D$ v* H$ P// Desc: & F2 N- a- s" c- j* k2 w4 @" i
//-----------------------------------------------------------------------------
) G0 D5 K+ L; r7 S) t# ivoid CMazeServer::Shutdown()
M' q9 r$ Y7 R9 i{
! A! v1 j$ ^$ W& M W}</P>
- a' ~; n( I* E1 D' T% D7 P+ A4 s% e1 Z8 G. W
< >
! i. j9 J {% d* s3 i; |/ ]( ~# A! Q//-----------------------------------------------------------------------------
" {% n: Y( e4 y: W// Name:
, Y" N+ m+ c# B0 C4 }5 S) m// Desc:
% K+ y/ V/ f# k2 R. x//-----------------------------------------------------------------------------2 G8 \( y; h# d$ i: E" U+ w6 O
void CMazeServer: ockRange( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )# c' W* B( }4 p7 ^$ I
{
, O, f- K. V0 \( F+ S m_LockGrid.LockRange( x1>>m_dwMazeXShift, y1>>m_dwMazeYShift ,
9 W5 e& m: Y3 \! G3 V1 M+ y) i x2>>m_dwMazeXShift, y2>>m_dwMazeYShift );8 b2 `" u" y6 t0 V. r- r( o
}</P>
% q8 [) x# J) E2 N
6 r/ Y$ f) P+ H* B< >
1 E- B6 R: e3 s# l) t1 ^//-----------------------------------------------------------------------------, Y- I2 Z' q9 M; A/ H: m- l
// Name: ( A, x3 O$ {# G* Z0 B0 i* \- L. ^
// Desc:
) z1 S" q9 A9 u# y4 H: B. {//-----------------------------------------------------------------------------! H: W q+ i+ `. D4 t# T
void CMazeServer::UnlockRange( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )
& a F ?" ~$ e% y/ g) G/ X1 J{$ i b: E2 L2 W1 F- \ k/ c( ~
m_LockGrid.UnlockRange( x1>>m_dwMazeXShift, y1>>m_dwMazeYShift ,+ G" P& k7 p. _$ s9 g
x2>>m_dwMazeXShift, y2>>m_dwMazeYShift );
, r; ]" l% u/ V2 S# A g3 j}</P>9 i7 n# w+ E# ^* L; |
/ y: G M. L7 h; R
< ># H3 `. N6 f( r& A( z5 k
//-----------------------------------------------------------------------------5 Y, l2 `$ l" E# a1 a6 }0 T) t! q
// Name: $ v. u/ [' U' p- K' P" ?+ k
// Desc: " p* r7 u! B* h- L! O' K
//-----------------------------------------------------------------------------5 a U" R6 S8 ?! J4 N
void CMazeServer: ockCell( DWORD x, DWORD y )( m9 `; ?! i) c, `& W% _
{
$ A& o& ~. |1 B! ]8 G if( x == 0xffff )
2 v6 B7 h, J" w& a$ c$ Y m_OffMapLock.Enter();
! L# Y- B" G! o" U- E; Q# r else/ k" D8 F* d: i6 o3 K
m_LockGrid.LockCell(x>>m_dwMazeXShift,y>>m_dwMazeYShift);
5 J. `* E- L/ `* h}</P>
- i0 z# O3 y' Y9 U& H6 m* o
0 k4 |3 P& Y2 ]; Q! Z< >
& n. {/ k* u# w9 T% i8 x+ L# _//-----------------------------------------------------------------------------; s9 @( j( G* I' Z- N% }1 R
// Name: ' r. V e7 d# N2 L. F: f
// Desc: / n: \, {' e7 m9 F( d
//-----------------------------------------------------------------------------
2 M2 s8 J; w' k: Ivoid CMazeServer::UnlockCell( DWORD x, DWORD y )
1 t$ c2 v$ S2 f9 y& F+ U+ e{
3 W# r9 P! G( x$ m7 u& R/ a& { if( x == 0xffff )4 X: x* Y' |6 O; B6 j
m_OffMapLock.Leave();+ I7 l8 d% a7 p, ~, _% B
else
. I/ j0 u& [, _5 S o m_LockGrid.UnlockCell(x>>m_dwMazeXShift,y>>m_dwMazeYShift);
1 E. F& K+ ]* c4 B}</P>
! x7 Y, i1 V0 d
/ e+ Y; C1 f2 d) ]1 x< >- Q( ]6 U6 r9 F6 `3 Z
//-----------------------------------------------------------------------------
]$ R- F0 W5 \/ x; ?& m// Name:
5 J$ k# }9 o1 e* z9 i6 k) `) d// Desc: 4 w& B# y; l6 M! T
//-----------------------------------------------------------------------------* q1 Z1 H' V6 G1 _6 ?2 p( d
void CMazeServer: ockCellPair( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )
" I; z2 J5 \1 G c3 Z/ r7 K+ ^7 p6 o{
' A& f4 E0 I* M ~ if( x1 == x2 && y1 == y2 )
5 F1 [0 v4 Z/ C6 n& ]; ~" @ {
; h6 f: }1 _. `$ m2 x4 c3 ~, x if( x1 != 0xffff && y2 != 0xffff )+ V: D% p5 M3 S" j- I0 e$ f- Z3 H# j
LockCell( x1, y1 );
+ a$ `% }2 a2 } else
; N* l% Y# K q1 i m_OffMapLock.Enter();</P>
) x& p) f r* s, T) J/ Z< > return;' l" N$ N6 x! i) J$ p
}</P>
0 i6 u6 b( E$ g1 N( f; c d- F< > DWORD x1shift = x1>>m_dwMazeXShift;- m: R7 Z7 a$ X' x
DWORD x2shift = x2>>m_dwMazeXShift;
3 @/ _ U: E' v9 T K, k- l: C DWORD y1shift = y1>>m_dwMazeYShift;
, Q8 f D& U n4 F DWORD y2shift = y2>>m_dwMazeYShift;</P>* A, E4 d. n- K* D
< > if( x1 == 0xffff )% D: T2 V! R7 f6 R
{0 K7 K3 ]7 n; ?+ o/ c( {
m_OffMapLock.Enter();
- {% @0 _& q7 l& z4 l$ F9 _6 x6 z m_LockGrid.LockCell(x2shift,y2shift);
+ e9 P! @9 ?& K4 m' ]4 t$ Y }
5 G2 V( H& w1 E4 F3 f7 V else if( x2 == 0xffff )! s' x, ~/ ~! @4 I
{
9 C1 j2 \! n4 j m_OffMapLock.Enter();5 S' U; d$ d9 u1 r9 m" {/ Z6 x
m_LockGrid.LockCell(x1shift,y1shift);2 Q8 V p( a5 |$ [# K
}7 E9 l5 U0 e" u3 X9 Z+ |3 L4 E
else 8 e( g( ?; F: l; ~+ @' t
{
6 Y6 r' X; F( Z( p$ X m_LockGrid.LockCellPair(x1shift,y1shift,x2shift,y2shift);8 w% ?3 H* z( |& n1 o
}/ H. Q6 E. X( o3 G5 F
}</P>: e$ S P( r5 {: K- k
8 P( s, K2 i, ?3 W< >( s' p# R: A6 @) z: A+ w) E
//-----------------------------------------------------------------------------* T# g, @! L. X5 y; d" h
// Name: ! \* ?. _% n/ I$ G2 g' G' z' [
// Desc:
0 ~" F3 T# G! X9 b2 L" E1 n//-----------------------------------------------------------------------------
& D8 u( K U+ s% ?/ L! B& ]" d" Wvoid CMazeServer::UnlockCellPair( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )
# }5 c& A8 N& b{
8 p7 T5 b9 u! p' ]! f if( x1 == x2 && y1 == y2 )+ h* {4 T9 I* @; X4 e7 |1 N6 x
{( q! L, s3 j/ w, w4 J* L3 P
if( x1 != 0xffff && y2 != 0xffff )
) }+ [* e. c4 a2 L+ m9 O B UnlockCell( x1, y1 );0 {+ Q4 @% w9 g8 {: z, a/ K/ _
else
* G( o$ F/ g# \ m_OffMapLock.Leave();</P>
& L0 l! A" G2 q; B3 p* P1 U< > return;
# w: H" e. l( T' d/ P+ l0 S }</P>
2 C \. q( T0 i4 B+ S<P> DWORD x1shift = x1>>m_dwMazeXShift;
% g0 z* z4 d$ W. ] DWORD x2shift = x2>>m_dwMazeXShift;/ N, h6 W' I. t
DWORD y1shift = y1>>m_dwMazeYShift;
9 Q# M: Y5 |8 k& _* v8 y DWORD y2shift = y2>>m_dwMazeYShift;</P>$ d% }, h4 B* { W
<P> if( x1 == 0xffff )
% e: B2 x6 m5 S3 F {7 H9 F( `$ U1 [8 M4 {, v! q4 F5 m
m_LockGrid.UnlockCell(x2shift,y2shift);
8 E# x- V5 K/ ?! G m_OffMapLock.Leave();& a/ h$ m9 r% w4 c5 e
}4 V# P& n9 _4 y# [! Y7 j
else if( x2 == 0xffff )
5 G/ T" V* `) ?" ^ {
% K# i* g8 l; I m_LockGrid.UnlockCell(x1shift,y1shift);+ G; C& [: T; @" B) p
m_OffMapLock.Leave();
5 ~( _/ K! }% Y: |! f3 k# G }
1 z& d G# _9 ` Z% n- N- y else 1 P( {1 h$ ]9 m2 l3 V
{
1 T7 _2 F( A# @1 A4 V& l5 A m_LockGrid.UnlockCellPair(x1shift,y1shift,x2shift,y2shift);! q8 }" D/ X$ G
}
+ }% n; o- k, Q% Y}</P>
# \( f M P/ b8 e& d
. y" M" k# b4 _$ K( s3 h% Z<P>4 E6 w" U* ?9 m* l( Y: ^4 |5 V8 ~* I9 N
//-----------------------------------------------------------------------------
8 K7 V# d5 Z$ _( H) t& V// Name:
+ j2 ^3 z. l* Z/ q) D( }// Desc: . }4 n8 Q0 l0 d3 f. |, r
//-----------------------------------------------------------------------------
% E: c! q1 T' g7 T# E' _: ~void CMazeServer::OnAddConnection( DWORD id )9 t3 z5 I* K' D3 n
{( ^1 V. ~% s5 `; [# }) L* J
m_AddRemoveLock.Enter();</P>
: K) R5 _' D5 a- Q) T<P> // Increment our count of players4 s5 z/ }& e1 A& U& W/ A
m_dwPlayerCount++;
2 `+ ^6 R0 P: k2 a H) Z if( m_dwLogLevel > 0 ) V" U# D5 d/ N% P# ?2 Y1 B* V+ h
{, m; n# w6 `0 g6 o) n5 F
ConsolePrintf( SLINE_LOG, TEXT("Adding player DPNID %0.8x"), id );9 n0 w9 w) u7 v7 j4 k
ConsolePrintf( SLINE_LOG, TEXT("Players connected = %d"), m_dwPlayerCount );+ L! a$ g( ?6 V! N% j& Z1 s: w
}</P>
% c5 Y0 |3 [3 b3 k<P> if( m_dwPlayerCount > m_dwPeakPlayerCount )
# |4 r# a: b8 O @ m_dwPeakPlayerCount = m_dwPlayerCount;</P>/ `) T) G: j# V8 g2 ?$ k
<P> // Create a player for this client8 ]5 v/ F) c( s' J4 _4 F7 E
PlayerData* pPlayerData = CreatePlayerData();; x# ^4 N7 F- i6 `) [8 ]
if( pPlayerData == NULL )
! _' g" ]" W$ o {/ B1 {9 C* _. a# A, v4 a6 K! s
ConsolePrintf( SLINE_LOG, TEXT("ERROR! Unable to create new PlayerData for client!") );7 i1 X* w, m1 D& u/ l. e
DXTRACE_ERR( TEXT("CreatePlayerData"), E_FAIL );
( y; Z N; u" Y7 d7 g6 z1 i) B m_AddRemoveLock.Leave();, s, k2 ?1 W# m2 F! l
return;
; n& v8 `, T7 m w9 n6 i }</P>
7 L1 ]; |1 y8 h* x5 a; z( W<P> // Store that pointer as local player data
! D: y8 u9 J: d; t; }& \ SetPlayerDataForID( id, pPlayerData );</P>1 ~, m0 ]6 u" g3 a; x
<P> // Grab net config into to send to client, P. q: w3 ]; K0 k$ k8 \" L! e
m_ClientNetConfigLock.Enter();
2 c! U2 n U/ Y! ^# I ServerConfigPacket packet( m_ClientNetConfig );! Y/ X, P: X4 S5 |9 u9 ?. x1 H9 \
m_ClientNetConfigLock.Leave();</P>8 A6 Q% K5 A9 k- |
<P> // Send it
6 v$ b! S% z, d% _/ f+ ? SendPacket( id, &packet, sizeof(packet), TRUE, 0 );</P>+ t% @0 y' [/ c! {; _1 \6 e
<P> m_AddRemoveLock.Leave();
+ t2 {! B9 g1 s8 X% l}</P>0 R8 ~( d7 b& N% F/ k1 _$ K# d
1 b5 E0 N" s j' p2 Z J1 D. R<P>* C7 v9 W+ }/ L9 i( {; m2 n! x
//-----------------------------------------------------------------------------1 e0 j8 D* N6 {/ G
// Name: 7 H5 o( K. ]$ A
// Desc:
, @( D" p- d% z/ h0 |//-----------------------------------------------------------------------------+ X. e# `0 \; M" ], v% ]! y
void CMazeServer::OnRemoveConnection( DWORD id )$ L; v( [4 V- B+ k8 F
{ V* _) q5 B, F) ~
m_AddRemoveLock.Enter();</P>5 g: m7 |+ i Z% x. M
<P> // Decrement count of players
3 d; e9 |% \3 C m_dwPlayerCount--;</P>9 W5 J3 Y$ I( ~
<P> if( m_dwLogLevel > 0 )4 c; `/ X+ P- ~
{9 {! q" m, o2 d9 G9 p0 }$ W* n
ConsolePrintf( SLINE_LOG, TEXT("Removing player DPNID %0.8x"), id );
5 v, W6 i- a6 w ConsolePrintf( SLINE_LOG, TEXT("Players connected = %d"), m_dwPlayerCount );
; j: Q! y* e8 d) X+ y: z }</P>' H3 j% |, n! b+ j- K0 A" n
<P> // Find playerdata for this client
8 w. ~$ ]+ t2 U/ Y. \ PlayerData* pPlayerData = GetPlayerDataForID( id );
& j+ e- M, H; r& o# k+ `7 W if( pPlayerData != NULL )
% ?8 l9 X3 q2 t% o1 ?) w1 V9 B9 B {
/ n+ C1 Y! P' e5 V3 { // Destroy it3 `& L; _/ U' `! Y' R
RemovePlayerDataID( pPlayerData );+ {+ G/ w) _8 I: C8 y! r2 d( B
DestroyPlayerData( pPlayerData );
1 O0 u& i+ b9 n5 q }</P>3 Q( X$ l: m4 p" C+ a
<P> m_AddRemoveLock.Leave();
- y& J5 t! P; D; Z# a}</P>- e( J0 f0 P" h
8 E9 f I+ A7 H% {) F<P>
" Z/ ]3 u: _- p2 `/ T* }//-----------------------------------------------------------------------------, T* q0 O! ]& o x
// Name: $ s. G$ M7 @6 p- G4 o6 G
// Desc:
( T1 Q' a& c3 h' o' ]% F% ?! ?//-----------------------------------------------------------------------------
6 F6 V. A, L3 O6 JHRESULT CMazeServer::OnPacket( DWORD dwFrom, void* pData, DWORD size )
" d9 _# `; w# }3 y6 U+ V{5 M, v! G/ u4 `9 B/ r) n! {
BOOL fFoundSize = FALSE;</P>
4 w- Y- Z3 G# I4 H2 E0 g* F P+ E<P> // Increment the number of thread we have in this process.
6 K# J4 `/ p2 m- q m_csThreadCountLock.Enter();</P>
1 S# W3 d* B; f* L! s<P> //Get the start time of when we entered the message handler.5 E* `( a4 N H, B& }, Y# t9 f
FLOAT fStartTime = DXUtil_Timer( TIMER_GETAPPTIME );</P>
: M7 g- n. X L; ^, E% ]; O2 N<P> m_wActiveThreadCount++;
9 X5 K8 I N3 @9 V7 q% i: x: e if(m_wActiveThreadCount > m_wMaxThreadCount). l( \* k( O7 u( {( |
m_wMaxThreadCount = m_wActiveThreadCount;
# v$ C4 y+ _- Y# N# Q2 n
, a* V7 k; I/ }) U1 o6 g* F$ N6 | // Calculate and average.
* |: u1 c5 U8 R4 m1 W FLOAT fdiff = m_wActiveThreadCount - m_fAvgThreadCount;
* w0 [( Z8 ~4 e, D: u& B- N7 j m_fAvgThreadCount += fdiff/32;3 g9 Y+ T$ g% c3 A
1 X+ d2 z: f$ D/ S( B3 t8 Y
m_csThreadCountLock.Leave();</P>
$ e$ y* p2 {. a9 U1 O<P>
7 }6 v8 `) o9 D0 C ClientPacket* pClientPack = (ClientPacket*)pData;
" L0 _1 s" H2 l) R$ H switch( pClientPack->wType )' ~$ O' z G' r4 `2 g
{
4 N% j8 q6 | P, `5 \$ s0 n% d i" T case PACKETTYPE_CLIENT_POS:; W* [$ o4 r- I0 F
/ @) B8 ]7 S& z- N8 s1 ` // Check to see if the packet has a valid size. Including
' |7 `* A9 V* Q" e( h // the custom pack size.+ P( {; O* X% {
if( size < sizeof(ClientPosPacket))0 B4 ~9 m5 ~5 ]6 [2 q$ A1 y! Y5 Y. p
fFoundSize = FALSE;2 X; U, j9 s) r; A8 U
else if( ! IsValidPackSize(size - sizeof(ClientPosPacket)))# n! M! v/ \4 N+ \3 w' u5 }( k+ e1 r
fFoundSize = FALSE;. b. p0 J# |) o' n1 |( ]# |4 x! y$ D
else
( `7 p+ a# q4 I/ C fFoundSize = TRUE;</P>
J$ v, ^" R8 C$ h7 _<P> // If valid sized packet, handle the position.
$ W& m: f5 h! K3 Z; n if(fFoundSize)- a# L$ d6 q. Q6 T& X" c+ X3 c
HandleClientPosPacket( dwFrom, (ClientPosPacket*)pClientPack );8 ~/ D8 H* c- k: _ _5 I5 |
else
0 ?5 m3 o5 `1 m, r m_pNet->RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );</P>3 m- z9 f4 e! i, ]& g+ T
<P> break;</P>
- W' ?5 b1 M& U4 A<P> case PACKETTYPE_CLIENT_VERSION:" B% ]5 j% g, {
if( size == sizeof(ClientVersionPacket) ), h1 K( p9 y- _6 W8 K! e$ `! A0 s
HandleClientVersionPacket( dwFrom, (ClientVersionPacket*)pClientPack );
# Q, d$ d5 e Z& R else
; s5 S* C2 s2 E& P! u m_pNet->RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );6 U& E7 A7 ^2 f2 q! J
break;</P> M3 f# A' `0 J" m" c: c, s3 |
<P> case PACKETTYPE_SERVER_CONFIG:</P>
; \% l4 C2 ~0 T5 |7 h( r; M4 T6 r6 E<P> // Server config packet sent to all users (including server). Just ignore and continue.</P>/ ~/ C9 l* Z. p: s' v
<P> break;9 ~/ {$ m: w# [
default:2 R- A$ m. g( G$ @; d& x6 f3 p" v# P0 P
HandleUnknownPacket( dwFrom, pClientPack, size ); \# r9 R, T, [6 t
break;6 h/ t2 ~5 Q; \
}</P>& L/ q. e1 q! G
<P> //If the user wants to hold the thread, Sleep for given amount of time.% s! i7 Q3 E$ r. S
if ( m_dwServerThreadWait > 0 )+ Y @& X/ V5 z! f
{- B3 Y5 ], g/ Z, X% t* ^
Sleep( m_dwServerThreadWait );( I( ?7 m B3 a
}& L8 ]* U6 C' x0 Q0 l/ A1 L
" S$ G/ k f+ @- R; s( t- w
// Retrieve thread data for this process.
0 z. N0 r! M" b( p: P+ p m_csThreadCountLock.Enter();</P>
: D( | j% \3 i* F X5 `<P> m_wActiveThreadCount--;</P>
2 r! b& L3 K$ [% |# H2 @: l; T<P> FLOAT fDiffTime = (DXUtil_Timer( TIMER_GETAPPTIME ) - fStartTime) - m_fAvgThreadTime;. r+ z6 X" r) P; Q
m_fAvgThreadTime += fDiffTime/32;</P>; Z, n* A6 I8 x; v5 d
<P> //Get the Max time in the thread.
/ [( b- {2 [+ t- Q* r if ( fDiffTime > m_fMaxThreadTime )
% i! X2 ~% L5 |; D% D0 |9 N {
6 z/ M+ f+ T. w1 N" z m_fMaxThreadTime = fDiffTime;
/ M0 z. ]$ [) A" C. ~5 Z }</P>. Y9 k) f; a" p" h. o$ ^( Q2 J
<P> m_csThreadCountLock.Leave();</P>/ ?2 _( v% n: s/ L# K
<P> return S_OK;
% g3 i: n+ V/ J2 @}</P>
/ ^5 i% W7 `" V- l. Y3 @! C3 | G" d: O$ m
<P>//-----------------------------------------------------------------------------+ a2 y, d( C" T
// Name:
8 Z- v7 A$ v7 d& w// Desc: , ^7 G1 d9 V" ?6 k0 [2 v4 m
//-----------------------------------------------------------------------------
! m9 ^( L8 E8 e8 ]4 y; c1 c" eBOOL CMazeServer::IsValidPackSize( DWORD dwSize )1 o% i; V. I2 U( `- G
{1 p! a- {' i0 t' {- h G* L+ {! T2 [
BOOL fFoundSize = FALSE;: I$ p& |/ x* i' z% p
BYTE ubPackLocation = m_ClientNetConfig.ubClientPackIndex;! R8 N8 i) [+ s/ ]& [
6 b3 N# R2 ^0 L! F* E2 u* S4 n
// Check through the array of valid pack sizes.& O" q- e' Q0 R7 X6 a6 z- R
if(dwSize != m_ClientNetConfig.wClientPackSizeArray[ubPackLocation])
! m0 j7 y: _, l- }0 q {
N2 w9 s/ u& `( n+ W3 M- z7 [ for( --ubPackLocation; ubPackLocation != m_ClientNetConfig.ubClientPackIndex; ubPackLocation--)/ L, o- L2 s m4 h. u6 w9 S
{) D1 B" U; Z* ~& w$ L
if(dwSize == m_ClientNetConfig.wClientPackSizeArray[ubPackLocation])( i7 p3 Z% Q. x. g1 Q# l
{" f9 g) p/ E- C6 Q; i Z" `. a! J3 _
// Found valid size in the array.
! R- D9 s( B' @- \2 `, v fFoundSize = TRUE;
6 T/ X; U5 j4 d- c break;8 }# i2 @! j2 t2 d0 I3 J- h
}
. J% \) a3 }8 v- M X if(ubPackLocation >= PACK_ARRAY_SIZE) ubPackLocation = PACK_ARRAY_SIZE; //Wrap the array." v, c u. D( \2 V
}
' _& e5 A, y; C& G+ l }: a0 o0 R. l5 E }- G: J
else
' x! q, \0 ^0 z3 {& X3 [' u+ e {
) B: O! u! f! ^5 K: j5 j6 K fFoundSize = TRUE;* k" C" n: t! k/ g5 n2 A, [1 J5 i
}</P>% g: C! r# d7 n( w& M, y
<P> return fFoundSize;: M: c) J5 m% b& a+ K: R) S% w
}</P>, B4 s0 S. ~7 Y
<P>1 ~0 ~7 X" C2 N/ z; q# l5 b1 U1 U+ _
//-----------------------------------------------------------------------------
* K6 J1 |! _4 T: `9 [// Name: 9 ?8 c/ O- j1 l! a' q2 C- b
// Desc:
3 Y& p. r( G( j( T! P+ E//-----------------------------------------------------------------------------5 E b {& n& S: n7 l: L9 H
void CMazeServer::OnSessionLost( DWORD dwReason )& R' T( ?- _3 m0 r
{
3 i4 V4 {4 o1 c: _ ConsolePrintf( SLINE_LOG, TEXT("ERROR! Session was lost") );
* X2 k' G, c. z6 B5 X& g! n}</P>6 g3 t7 n& t' Y7 k; a
7 R$ `3 I. t) ?% R
<P>2 N$ x! p5 q% e0 V" I8 \% ^5 M- p
//-----------------------------------------------------------------------------1 ]4 Q3 z, w4 o. z/ Y* |
// Name: . P1 V& R6 k6 Z7 M7 S/ P" H
// Desc: / R f3 F- |) C4 g4 y5 ]
//-----------------------------------------------------------------------------
' Z+ c* r9 H. c: E0 q" S% CPlayerData* CMazeServer::CreatePlayerData()/ B0 k) a4 v7 W
{" G M8 _' g4 c+ k8 c$ N9 N! r
m_PlayerDataListLock.Enter();</P>
; y' m, m8 P0 ]- N<P> // Grab first free player in the list n! m2 X- l, K( l
PlayerData* pPlayerData = m_pFirstFreePlayerData;</P>
; o6 H& V9 m5 b+ ?8 e<P> if( pPlayerData )7 [4 P8 ^1 a* t; Y1 E
{, [: \7 w8 \6 |- G; S
LockPlayerData( pPlayerData );</P>1 t# z5 m! W: t3 s7 B
<P> // Got one, so remove it from the free list
% O( [' H- w B" T- c if( pPlayerData->pPrevious )& S# ~, u& ]* l: ?" m
pPlayerData->pPrevious->pNext = pPlayerData->pNext;
5 T, ? {0 v* R6 | if( pPlayerData->pNext )
' a- V/ A0 i8 z. M" J. X. [3 G pPlayerData->pNext->pPrevious = pPlayerData->pPrevious;
5 `9 Z$ W5 W' r; C+ R/ ?2 ` m_pFirstFreePlayerData = pPlayerData->pNext;</P>+ _/ i5 }. Q; C3 U' D$ s3 f! d% z
<P> // Add it to the active list8 E' Y: r7 [* @9 u% G O
if( m_pFirstActivePlayerData )
c. ^& C$ H: _! R" |/ [ m_pFirstActivePlayerData->pPrevious = pPlayerData;4 E$ N" m% y& ]- X C
pPlayerData->pNext = m_pFirstActivePlayerData;
& Q0 g; k6 A; W2 s6 I pPlayerData->pPrevious = NULL;7 ^% t+ V& E1 K9 Y" V* o- k& @
m_pFirstActivePlayerData = pPlayerData;</P>' e d# r0 t' R% t
<P> // Update count of players6 u7 a7 V# z6 w* |5 e
m_dwActivePlayerDataCount++;</P>% J( R9 L6 Z4 e7 a8 Q$ x# X
<P> // Generate the ID for this player
: {# R0 l( j) y. L/ [! T m_dwPlayerDataUniqueValue++;; G' G \5 s5 ?1 A
pPlayerData->dwID = (DWORD) ((pPlayerData-m_PlayerDatas)|(m_dwPlayerDataUniqueValue<<PLAYER_OBJECT_SLOT_BITS));</P>
! v. L- O3 n# }: D2 D% S. c<P> pPlayerData->pNextInIDHashBucket = NULL;2 z- P9 u; b0 q/ f6 Q* m( d% B
pPlayerData->NetID = 0;
& g1 P: w6 r+ y. O2 X( p2 H pPlayerData->dwNumNearbyPlayers = 0;</P>' o3 ?4 c0 ^$ y- B0 C
<P> // Insert into the "off-map" cell: i/ ~1 }( ^0 z0 L6 _: m! A
pPlayerData->fPosX = pPlayerData->fPosY = -1;
h4 Y( B! q+ D# T pPlayerData->wCellX = pPlayerData->wCellY = 0xffff;
: |% W; [8 x7 q9 @& w" o m_OffMapLock.Enter();
0 d* J j" ?8 O6 u! B( G- K pPlayerData->pNextInCell = m_OffMapCell.pFirstPlayerData;
1 w* { L" d3 @: ]' U( Q% ] m_OffMapCell.pFirstPlayerData = pPlayerData;+ \% l; R! M- `7 E# o# }
m_OffMapLock.Leave();</P>2 W3 ]/ ? K1 ?* n+ ~3 z
<P> // Mark as active' U2 ]/ R! O4 K/ x4 F
pPlayerData->bActive = TRUE;</P>
# T6 [7 q" u L<P> UnlockPlayerData( pPlayerData );
8 m1 B; o9 h F8 t }</P>* N* I9 K; r' t7 e0 R- P) M
<P> m_PlayerDataListLock.Leave();</P>* l6 ?( D; e0 h. _& F
<P> return pPlayerData;8 R C5 P0 H5 C6 _, c- @& z
}</P>' _- N9 o% M! ^5 n: h$ B1 ~
/ l; ~9 L: z5 J0 A+ `0 t0 S r<P>0 h1 y. F8 q3 I
//-----------------------------------------------------------------------------7 f: a5 U% _( Q7 e/ m
// Name:
+ h. R0 V. L5 E; K7 H! e// Desc: , b. P; j" [) u; D! i
//-----------------------------------------------------------------------------9 L9 m5 H3 d S) H# q
void CMazeServer: estroyPlayerData( PlayerData* pPlayerData )
/ }) S5 r' D: V# i' j5 a{
- S8 P/ C" E$ M& m$ x' B, O m_PlayerDataListLock.Enter();( [, }& T- r' F2 W' H
LockPlayerData( pPlayerData );</P>9 l' u; M. n% g- h N& E
<P> // Remove the player from its cell
: g; i& J1 a9 e' V. x: ], S RemovePlayerDataFromCell( pPlayerData );</P>
0 |3 K) q; t+ X7 a6 F. a7 d7 B<P> // Mark as inactive
5 [1 @- A. m" p% u+ g9 j7 ]* R, q pPlayerData->bActive = FALSE;</P>
/ b U, U7 O+ o* t<P> // Remove player from active list! Q0 l2 {+ Z' {7 Z
if( pPlayerData->pPrevious )3 l: P* |9 c& M4 h& y: S
pPlayerData->pPrevious->pNext = pPlayerData->pNext;5 `) g. ~1 R0 H: {1 _
if( pPlayerData->pNext )* X8 a* t" n d
pPlayerData->pNext->pPrevious = pPlayerData->pPrevious;</P> m- N& D8 g' i; P
<P> if( m_pFirstActivePlayerData == pPlayerData )
0 _# m* [ v* f# T9 { m_pFirstActivePlayerData = pPlayerData->pNext;</P>
" f" M( V: K% c<P> // Add it to the free list
& N/ f4 h( o: ?+ F if( m_pFirstFreePlayerData )
+ A4 P) z+ i8 Z* l m_pFirstFreePlayerData->pPrevious = pPlayerData;& u) `3 G$ M6 n* R! U! w0 Z* b
pPlayerData->pNext = m_pFirstFreePlayerData;
, x& q' S* P( b. o3 y s% Y/ f pPlayerData->pPrevious = NULL;$ @; P" x0 Z6 [/ b/ o+ g! M$ a7 B
m_pFirstFreePlayerData = pPlayerData;</P>
/ ]7 Z9 G. V5 Z/ j<P> // Update count of players" d7 S0 ?' E' a5 ^0 a
m_dwActivePlayerDataCount--;</P>
; O% A" }* A! f4 H7 q" D6 g<P> UnlockPlayerData( pPlayerData );
% |0 T% V. c) }/ { m_PlayerDataListLock.Leave();
) |% t, _7 i+ p}</P>
8 j/ x& P6 {; Z' I1 h) Q% m8 c& @, I
/ S* \8 b* a4 G, [' G/ Y1 f: T" @9 R<P>+ J9 m0 r( l4 I' A j! j; g, V% k1 D
//-----------------------------------------------------------------------------
( q1 T: I( ^! J) f+ w# M" q// Name:
- B( U3 X, j+ H t ^- w// Desc: V6 Y5 Q6 G2 Y) Z( R9 C
//-----------------------------------------------------------------------------
8 h0 [6 L5 [* K9 ]void CMazeServer::RemovePlayerDataFromCell( PlayerData* pPlayerData )( k& H2 P" V$ g' W
{4 V. p V( }0 ~3 l- q( a
// Lock the player
Z/ d6 \9 a. I' P \3 |! k6 n LockPlayerData( pPlayerData );</P>
: V, x" x/ [* ~3 Q- ~<P> // Lock the cell the player is in
+ @6 X$ k- v$ q ServerCell* pCell;
7 ~+ l: U. [& T- L3 p! n' t, e+ @ if( pPlayerData->wCellX == 0xffff )3 _) o9 Y" g, Y7 ^; O
{5 v1 X3 T4 G# C4 D1 }
m_OffMapLock.Enter();3 X% T" V, I- w9 F6 P6 X! M
pCell = &m_OffMapCell;7 _0 p3 D+ f% m$ T$ \
}: f; X6 v4 q u, d/ k6 o
else
* E/ p. y2 m( M/ \ {+ H5 w* _* r# X: c- m* @# U
LockCell( pPlayerData->wCellX, pPlayerData->wCellY );) }4 {, J! b9 D' r- |8 E. `0 q2 L
pCell = &m_Cells[pPlayerData->wCellY][pPlayerData->wCellX];
/ s6 v$ ~8 ^3 ?8 ^2 i }</P>$ j. i0 e4 u( f
<P> // Remove it from the cell
+ l0 \& E" d! O, C PlayerData* pPt = pCell->pFirstPlayerData;
4 m" |# l7 P( ?0 T, o( u2 Z PlayerData* pPrev = NULL;
6 Q+ `$ t" T0 Z. b1 m while ( pPt )
) H. m$ q! M4 v4 O {: y: ~- w6 G0 t$ U. F
if( pPt == pPlayerData )
3 J% f& m6 p5 P# Z1 |2 g {) c1 m& Z( d) E. T1 K0 f1 X0 Y. E
if( pPrev )
6 K: a, @3 B" ?% Z' j$ p) } pPrev->pNextInCell = pPlayerData->pNextInCell;4 B( \6 u% x: c) K! a' T/ ]
else
- \' }1 [" D# J o5 U* Q3 ^ pCell->pFirstPlayerData = pPlayerData->pNextInCell;</P>
8 y! T0 Z, ^) S<P> pPlayerData->pNextInCell = NULL;( i5 w# O' D; c6 D1 I8 x" M
break;
4 `8 a( Y7 |6 K& o3 F0 O0 R* `# D }& {, s; m7 w3 \, J, f" s) N+ R5 J
pPrev = pPt;4 o2 G; o9 t3 V/ @8 F6 a( Z
pPt = pPt->pNextInCell;( E) @- D: N7 ]( `8 W9 h8 g
}</P>! U; c x1 a& K P- [
<P> // Unlock the cell& r) S) C8 r8 X4 P4 ^% U: }
if( pPlayerData->wCellX == 0xffff )! c2 {+ [( z& _2 n) k* f5 S! D9 X
m_OffMapLock.Leave();
( n* E' M ~2 h; B6 J1 s( ^ else& U1 j% r. c, u5 }" E! I! J' {$ o
UnlockCell( pPlayerData->wCellX, pPlayerData->wCellY );</P>- k3 c) j/ ~1 I) ?2 R* Z9 g
<P> // Unlock the player
5 p O% U* G3 } UnlockPlayerData( pPlayerData );% m0 a0 Q9 l5 F1 g$ o( X6 o5 u' ~
}</P>0 o$ L# @' G5 u. O
. E' a5 w+ X; k' }' f M0 m3 Z
<P>: W! r/ a B- Q' k1 T" F
//-----------------------------------------------------------------------------6 I7 r0 H, G+ J! v. F' g
// Name:
7 Y8 y! H2 l; F+ S, f& @# H// Desc: " X+ `. Z. x1 z' l+ t! G
//-----------------------------------------------------------------------------
?; {% n6 f0 Y, s- Lvoid CMazeServer::UnsafeRemovePlayerDataFromCell( PlayerData* pPlayerData )4 ^/ h1 g0 P9 b j6 j
{
& h3 q5 H4 c. ]" j2 e0 t v ServerCell* pCell = GetCell( pPlayerData );0 @: P0 d0 N" E: v. Z0 q
PlayerData* pPt = pCell->pFirstPlayerData;
/ O# N8 F# S9 ] PlayerData* pPrev = NULL;
% Q! U( m" Z* c while ( pPt ): R Y- \- V1 _3 e
{
! k, l( M& O% L4 c+ m+ B if( pPt == pPlayerData )
# ~& M7 K H3 q2 ?4 h {5 e4 {4 L8 U& b3 G& ?
if( pPrev )3 t1 e7 T' t8 Y& h
pPrev->pNextInCell = pPlayerData->pNextInCell;
) _8 x! L! s& e0 H! a0 v else) n& @; |6 R2 p: M% N
pCell->pFirstPlayerData = pPlayerData->pNextInCell;
: L; X2 ?$ p# u1 B pPlayerData->pNextInCell = NULL; T' b! P1 z( C* r0 d1 `
break; h4 a7 G7 t( n+ H1 Y# W
} o ]. E# ~2 v- Y5 \/ _5 F
pPrev = pPt;, E( V Z3 w$ Y% z% u" ~
pPt = pPt->pNextInCell; ]$ `1 h4 o! W7 a( L' `" ?
}
c- f. B2 T+ o& ] I# H Y ~0 Q}</P>* L8 o+ ]5 j% k: }
' L5 {7 A/ G* ~7 } l s* W<P>
2 n: d$ }7 j/ Q//-----------------------------------------------------------------------------
0 N0 L/ f# K% X9 Q) q' \: [7 D s7 i8 u// Name:
% g& w& f8 ]# N9 y% S- w// Desc:
) p2 T7 h, D. i+ h& k' ?//-----------------------------------------------------------------------------
5 N- T, b* y% E0 `9 [; B) g& ]void CMazeServer::UnsafeAddPlayerDataToCell( PlayerData* pPlayerData )5 ^3 x8 M- t4 [9 P
{4 H w, M6 S, q
ServerCell* pCell = GetCell( pPlayerData );
9 z2 k4 K) b8 C1 }. ~ Z pPlayerData->pNextInCell = pCell->pFirstPlayerData;9 b9 L% q- f0 t: f3 k
pCell->pFirstPlayerData = pPlayerData;
: S" M! D1 b- r/ c6 z# ]7 q}</P>- G" ~+ }8 f) \" R7 p
% [7 T/ x: a& f( R; D' T8 |9 Y# k<P>
* y# ~/ k$ D1 h" d' n% {6 a//-----------------------------------------------------------------------------; |7 Q1 a- C( f, R- F% D7 I
// Name: 6 p& Z" M; T% w0 ~/ I
// Desc:
4 a4 Y7 g& O, }+ ~9 Z//-----------------------------------------------------------------------------
q6 `# p1 ]0 N* l* c. B, avoid CMazeServer::HandleClientPosPacket( DWORD dwFrom, ClientPosPacket* pClientPosPack )
5 S3 `4 Y* d8 c8 {( E, Y{$ O4 y) G+ n' o0 {' {3 A4 y
// Grab player for this client and lock it! b9 k* \2 q# C+ B
PlayerData* pFromPlayer = GetPlayerDataForID( dwFrom );2 _( h, G) `) s
if( pFromPlayer == NULL )
- q! D5 g0 T0 |% x {
; q% V1 B, F9 H5 h if( m_dwLogLevel > 1 )
0 H) J! c; m8 B7 T1 q; [' q ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Could not find data structure for this player"), pFromPlayer->NetID );( t3 d* a( R5 E! n8 [8 {. D& Z
return;
- ]0 i* d; U7 T: i u) s* V }</P>
7 d0 `0 o+ t; `9 `<P> LockPlayerData( pFromPlayer );</P>
& {& `$ q- w/ c7 j0 m& @<P> if( FALSE == pFromPlayer->bAllow )
# {' |: j7 y! Y$ I# f8 {9 Q. Q {
: u& t6 W' |6 q t& H if( m_dwLogLevel > 0 )
! }& H4 L1 E7 v% G7 i ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Got position packet from bad client. Rejecting client"), pFromPlayer->NetID );</P>
9 @3 ^5 L: f, S6 a5 V: s<P> m_pNet->RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
, z% F3 o% }) c- T, ?3 h* m UnlockPlayerData( pFromPlayer );% j+ D: G; N! x; F$ d o, t% T% D
return;
4 F: q: D# b. Y! [ }</P>
' F" H+ ~6 X; G0 w0 B<P> // Compute the cell the player should be in now5 B# o4 x6 ]/ W
DWORD newcellx = int(pClientPosPack->fX);
. Q- e5 E. w- a$ a$ F6 l* p N4 F8 [ DWORD newcelly = int(pClientPosPack->fY);1 J2 M' H2 T! O ?! F6 S
DWORD oldcellx = pFromPlayer->wCellX;+ ^0 {8 P# m4 j1 v T
DWORD oldcelly = pFromPlayer->wCellY;</P>
- ~! _- m. n4 u$ Q* K! V$ [<P> // Have we moved cell?* F8 u4 `0 M( C$ _. W* ^1 b
if( newcellx != oldcellx || newcelly != oldcelly )6 i- K7 p e) S2 j0 {, [ O# y _
{
, ?8 J! f8 ~) M! o ^6 {( d3 ? // Yes, so lock the pair of cells in question
1 L. T5 o ]9 B/ e0 H LockCellPair( oldcellx, oldcelly, newcellx, newcelly );</P>8 g; [" u" C0 Y- l& w; h
<P> // Remove from old cell and add to new cell
8 O! s2 M+ p( r2 I% Q UnsafeRemovePlayerDataFromCell( pFromPlayer );9 Y( O2 v- B2 x/ v8 T! `
pFromPlayer->wCellX = WORD(newcellx); pFromPlayer->wCellY = WORD(newcelly);$ I: ^) m% X& e, `! z) v
UnsafeAddPlayerDataToCell( pFromPlayer );</P>
( x4 I2 L4 ]% R* V* T/ o<P> // Unlock cells
0 u1 u: P6 i: ]+ V" S5 b0 F UnlockCellPair( oldcellx, oldcelly, newcellx, newcelly );/ ?+ s7 e4 m1 Y" p
}</P>
* _ |' [7 z- r<P> // Update player position( \5 P; W, o1 `* J% X& J+ h
pFromPlayer->fPosX = pClientPosPack->fX;
6 A, C' L) G) w% y5 | pFromPlayer->fPosY = pClientPosPack->fY;2 L- i! d* W3 u6 F7 q) Q: x2 Z
pFromPlayer->aCameraYaw = pClientPosPack->aCameraYaw;</P>8 d9 Y4 m7 a6 K) ?# \! ~6 S* K
<P> // Allocate space to build the reply packet, and fill in header 1 |9 |4 K# g5 K; H. V1 J! U# v
DWORD dwAllocSize;
9 ]+ C0 ~9 P. B4 g) k* d% U K. ~ ServerAckPacket* pSvrAckPack = NULL;</P>) X4 }5 {# k6 Y! y+ m. W3 G
<P> // Begin by allocating a buffer sized according to
$ |) b- U. A' v! W // the current number of nearby players + 4. This will give - P6 j5 M4 c5 e1 g1 @/ o
// a little room for more players to come 'near' without resize: n0 f5 }. O) |; N2 D8 f
// the buffer.
# r" j) H2 W: c DWORD dwMaxPlayerStatePackets = pFromPlayer->dwNumNearbyPlayers + 4;</P>! I. ~- C& @5 D/ M7 r; g
<P> dwAllocSize = sizeof(ServerAckPacket) + dwMaxPlayerStatePackets*sizeof(PlayerStatePacket);
! R7 t+ c1 f- W, i2 _- k pSvrAckPack = (ServerAckPacket*) realloc( pSvrAckPack, dwAllocSize );
; q3 L S: L4 _9 g: H# f( \7 u$ i if( NULL == pSvrAckPack )5 Z7 j9 C) N, ^/ ~5 I) k
{2 ^% Q; k7 p; x" y7 S- H
// Out of mem. Cleanup and return7 l% {( C6 \/ m1 Q; F( I2 z, c+ s1 z
UnlockPlayerData( pFromPlayer );5 p9 t0 {4 `" T* u- ?4 H/ B+ ~
return;
; C& r( l* }! c }) O& \2 F- q! x. d) R9 G! z' o
ZeroMemory( pSvrAckPack, dwAllocSize );</P># }9 y$ O# O a8 @( E" |) P- j3 q: N7 Q7 v
<P> *pSvrAckPack = ServerAckPacket(m_dwPlayerCount);
5 |/ }( X7 c* l# w8 { Q& o) C pSvrAckPack->wPlayerStatePacketCount = 0;
' R% s2 m9 f. v* U PlayerStatePacket* pChunk = (PlayerStatePacket*)(pSvrAckPack+1);</P># B" x- h& D( {, |* m8 P1 `4 H
<P> // Compute range of cells we're going to scan for players to send
% D! F/ Z' d) h- b* Z4 q3 y DWORD minx = (newcellx > 7) ? (newcellx - 7) : 0;
6 n6 G) W0 M7 y; i) y DWORD miny = (newcelly > 7) ? (newcelly - 7) : 0;
: B! J6 g* C+ Z3 M DWORD maxx = (newcellx+7 >= m_dwWidth) ? m_dwWidth-1 : newcellx+7;
) M3 q/ g9 g# h9 K8 @ DWORD maxy = (newcelly+7 >= m_dwHeight) ? m_dwHeight-1 : newcelly+7;</P>
4 @9 ?: L$ \% Z<P> // Lock that range of cells% E; A7 [( k5 a( e4 x4 y+ z' S
LockRange( minx, miny, maxx, maxy );</P>
! @) D! t+ m* z1 m, E<P> // Scan through the cells, tagging player data onto the end of
0 O& }. c9 I. K! r% M // our pSvrAckPacket until we run out of room7 o) M {9 T$ ]/ I+ |3 J4 c
for( DWORD y = miny; y <= maxy; y++ )) x" Z& N& G' t' O! |) u! d
{# r3 T: {$ N/ \3 t% R. a& U3 s
for( DWORD x = minx; x <= maxx; x++ )
" [) v7 g# z0 s8 M { n4 D/ g$ `2 o; w0 B- g9 ?% C
PlayerData* pCurPlayerData = m_Cells[y][x].pFirstPlayerData;
$ V% l n. {+ [; N+ z w( o: A4 O4 G while ( pCurPlayerData )8 S0 Z7 I, o& `7 n; Z: `" M. K# Q
{
9 R/ G5 m j( x* G2 k6 D if( pCurPlayerData != pFromPlayer )- c/ u' y0 v( A8 q. g
{1 O/ }( U/ W# `% ]4 \: B
if( pSvrAckPack->wPlayerStatePacketCount >= dwMaxPlayerStatePackets )
- ]# m; H- @9 w# \+ y* x5 L {4 D0 K, w% B( h* z3 U
// Make sure pChunk is where we think it is
0 X% j3 f O- A& m/ \5 h assert( (BYTE*) pChunk == (BYTE*) ((BYTE*)pSvrAckPack + sizeof(ServerAckPacket) + pSvrAckPack->wPlayerStatePacketCount*sizeof(PlayerStatePacket)) );</P>! V; Z% K' @: n7 B$ y, l/ Z
<P> // There are more than just 4 new nearby players, so resize the
8 ?# e# j& p2 I: b // buffer pSvrAckPack to allow 16 more PlayerStatePacket's.
. y+ { q& g+ u* [3 |$ H! A dwMaxPlayerStatePackets += 16;
8 g( H( ?# A) ]7 M" b2 l: k8 { dwAllocSize = sizeof(ServerAckPacket) + dwMaxPlayerStatePackets*sizeof(PlayerStatePacket);0 }4 M: U1 k' N4 {# q
ServerAckPacket* pNewSvrAckPack = NULL;
, a8 |" r/ a# [& W. U9 J pNewSvrAckPack = (ServerAckPacket*) realloc( pSvrAckPack, dwAllocSize );- K2 f2 t, l+ T* x9 a) ~
if( NULL == pNewSvrAckPack )
4 e8 s6 s, T+ c& t {0 v$ U A: w/ {) P- A5 k/ T9 ]
// Out of mem. Cleanup and return
9 P+ V9 u3 T- t8 n free( pSvrAckPack );
6 a/ Q) W8 x" D( r6 w: u UnlockRange( minx, miny, maxx, maxy );( z2 }& G/ N$ r# m5 R2 f
UnlockPlayerData( pFromPlayer );. s" N, w3 p/ j6 Z6 |2 u% M* [/ o
return;
+ O: [# H& |2 D }</P>8 m J; c; h y8 U0 C
<P> pSvrAckPack = pNewSvrAckPack;
$ \ e7 j% s/ m pChunk = (PlayerStatePacket*) ((BYTE*) ((BYTE*)pSvrAckPack + sizeof(ServerAckPacket) + pSvrAckPack->wPlayerStatePacketCount*sizeof(PlayerStatePacket) ) );</P>
( U: u9 r% Z6 j<P> // Make sure pChunk is still where its supposed to be
$ i" j$ v$ T& b( c6 g assert( (BYTE*) pChunk == (BYTE*) ((BYTE*)pSvrAckPack + sizeof(ServerAckPacket) + pSvrAckPack->wPlayerStatePacketCount*sizeof(PlayerStatePacket)) );7 o) M6 p. s/ D% u9 l9 j
}</P>
$ V/ t& e4 u3 O<P> pChunk->dwID = pCurPlayerData->dwID;
* ]" D, M" [9 T& h+ C pChunk->fX = pCurPlayerData->fPosX;
- o1 i& t) l5 E% f1 a: q pChunk->fY = pCurPlayerData->fPosY;8 l6 ]0 @& O/ A$ W& |' l5 \
pChunk->aCameraYaw = pCurPlayerData->aCameraYaw;- X( V% J7 @# V8 v- Y
pChunk++;
0 @: c* d% X f& M; e8 q! A/ U pSvrAckPack->wPlayerStatePacketCount++;
: x# ?7 @& x7 {7 i7 Q4 v+ X: ~ }
/ _# F6 \# p6 z7 l5 J: N pCurPlayerData = pCurPlayerData->pNextInCell;& c6 u/ R+ W. |
}
. }; W& K( V2 x( J" @4 e/ h$ F6 ^ }
$ r" Z: W5 @/ ^( Y }</P>
( K! b4 r8 `) d. W6 z<P> // Update the dwNumNearbyPlayers for this player
- e( `* D- ]: M- @ pFromPlayer->dwNumNearbyPlayers = pSvrAckPack->wPlayerStatePacketCount;</P>
+ q3 R5 T" h$ r- `- m<P> // Unlock range of cells
$ e8 Q8 G6 r( t: ~ UnlockRange( minx, miny, maxx, maxy );</P>+ r g9 x3 Q( T* C" r( r& ?( x
<P> if( m_dwLogLevel > 2 )9 x' c6 i# N2 h$ \1 s4 B
{
G& S: L: n7 b$ G# ~ P# S ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Position is (%0.2f,%0.2f)"), pFromPlayer->NetID, pFromPlayer->fPosX, pFromPlayer->fPosY );( {& _$ Q& ^! a3 u
}/ k) _7 e. T+ S2 Y
else if( m_dwLogLevel == 2 )( j* H+ [* @2 v, ^6 K, [' m5 `: t
{
7 }; d1 v* |5 B6 t3 v- a FLOAT fTime = DXUtil_Timer( TIMER_GETAPPTIME );
7 f& |% k( [; K7 y, K if( fTime - pFromPlayer->fLastDisplayTime > 60.0f )) j% \* _+ U! H/ d% P
{
8 ~" s; w# M2 Q ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Position is (%0.2f,%0.2f)"), pFromPlayer->NetID, pFromPlayer->fPosX, pFromPlayer->fPosY );* V! K% K9 M& U$ |
pFromPlayer->fLastDisplayTime = fTime;
# B, K* z3 F/ p+ `& `+ U4 E }5 X6 q2 {. A! I3 R; q
}</P>6 I Z4 [2 r& M+ Z1 X/ w
<P> // Unlock the playerdata
2 Y+ l$ c: f2 M4 J UnlockPlayerData( pFromPlayer );</P>
2 t8 Z& H8 l( ]7 }<P> // Send acknowledgement back to client, including list of nearby players
+ ~; ]; u! F% a6 [1 g" ^, j DWORD acksize = sizeof(ServerAckPacket) + (pSvrAckPack->wPlayerStatePacketCount * sizeof(PlayerStatePacket));1 P6 f! i! j, [, z+ ?& x
! C$ a* K1 D- y* L% L& ]. Q
// Pack the buffer with dummy data.
; K. p, T8 Q( Z1 U4 F1 u if(m_ClientNetConfig.wServerPackSizeArray[m_ClientNetConfig.ubServerPackIndex] > 0)
8 g4 a' U, d/ r C/ o( s* |2 W {
& C5 A% _4 T+ T$ o% _' r DWORD dwBufferSize = acksize + m_ClientNetConfig.wServerPackSizeArray[m_ClientNetConfig.ubServerPackIndex];
% Y4 S7 p% \" @' [. o7 f) y: U& e VOID* pTempBuffer = 0;</P>
3 E7 \$ u7 \* E8 D" E2 k<P> pTempBuffer = malloc(dwBufferSize);
' e3 p2 f0 N% o if( NULL == pTempBuffer )5 z/ E8 G3 S* a( n5 A1 o" P
{
9 ]" M t* P: u0 V; P. F5 C //Out of memory7 U( r9 M( p! F. W. W/ S
DXTRACE_ERR_NOMSGBOX( TEXT("System out of Memory!"), E_OUTOFMEMORY );
+ q# `# j8 Y9 C2 R free( pSvrAckPack );
) c; _+ Q( s' P9 k, o4 A9 w return;
$ D" J( j' @7 ]2 ] I }</P>, ^* j- S( b5 r, n& C# N8 [
<P> FillMemory(pTempBuffer, dwBufferSize, 'Z'); ?$ d1 Z: P+ f; `4 t0 x9 s2 L4 z
memcpy(pTempBuffer, pSvrAckPack, acksize);</P>
$ C W6 T+ }7 [: R<P> SendPacket( dwFrom, pTempBuffer, dwBufferSize, FALSE, m_dwServerTimeout );; M4 H+ S! O t ?+ z
6 c7 k7 G0 `, k+ I$ Y; V free(pTempBuffer);+ }4 d' }; c) x3 @: A( E
}
6 N5 o8 v$ K+ D; Y/ P else
/ i& _9 P. R* u! K- _6 a: j {. r/ V* G7 C: r) j: O, `2 c
SendPacket( dwFrom, pSvrAckPack, acksize, FALSE, m_dwServerTimeout );
+ V5 ? N' @1 e }</P>. c8 Y i: @% ]: E
<P> free( pSvrAckPack );</P>
: v9 f7 I1 [7 s. g; l/ z0 g* h) _<P>}</P>5 V$ G: X; v* P: `6 b4 w6 K
7 N: {, m6 @: t+ p& U# e
<P>
, I: S+ V8 |: D5 N2 K E4 T//-----------------------------------------------------------------------------* K( P6 p) W4 m
// Name:
8 n1 |9 x H- v/ f2 i! d5 X/ m// Desc:
- `* {2 {% H4 S( `- `3 q9 h4 G) P! y//-----------------------------------------------------------------------------% i+ J- W0 {5 p+ t1 ~; O
void CMazeServer::HandleClientVersionPacket( DWORD dwFrom, ClientVersionPacket* pClientVersionPack )
$ h) ]2 _' G) H{, i7 u! x6 e: w2 P7 o
// Grab playerdata for this client and lock it5 v5 s- G! f8 q4 c& t
PlayerData* pPlayerData = GetPlayerDataForID( dwFrom ); `* _& i7 q5 g7 b1 x( Q* m! V; _
if( pPlayerData == NULL )1 n1 @8 g0 d( r( k3 z
return;
' g+ `8 B3 K4 a" E LockPlayerData( pPlayerData );</P>7 m4 W- |' \7 y% |
<P> // Record the version number : \ k& m. y9 j4 i3 |1 Z
pPlayerData->dwVersion = pClientVersionPack->dwVersion;</P>
5 U7 F+ b& C, Z0 n8 K: b<P> if( m_bLocalLoopback ). i/ \' c; q/ _8 t! [
pPlayerData->bAllow = TRUE;
2 z/ \ E% n+ U+ y L) a else7 u2 \8 d- Y& Z7 i
pPlayerData->bAllow = IsClientVersionSupported( pClientVersionPack->dwVersion );</P>( G F# G# T% w2 ^8 x2 k
<P> if( m_dwLogLevel > 0 )
0 S% Q' ^( B( N7 A0 |" | ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Client version=%d (%s)"), pPlayerData->NetID, pPlayerData->dwVersion, pPlayerData->bAllow ? TEXT("Accepted") : TEXT("Rejected") );</P># g% `; I$ v u- V6 d0 c
<P> if( FALSE == pPlayerData->bAllow )
* v/ I7 {; g' _2 B/ A% [ {+ \9 R* t$ j/ {9 m& \
if( m_dwLogLevel > 0 )
( Q& ?3 A2 @, J: f- @ ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Rejecting client"), pPlayerData->NetID );</P># A$ t# @' R6 }9 y* `. L
<P> m_pNet->RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );9 N" i+ \" m; K# F
UnlockPlayerData( pPlayerData );
0 B. W* e8 u9 V4 N( r return;' S: v. U/ w3 x. W$ s* V5 F
}</P>
) s) m$ D% ~- K' D<P> // Unlock the playerdata
3 o& A& Z% k# g: e! @- a UnlockPlayerData( pPlayerData );</P>0 y) R; L6 ]% j1 r/ A
<P> // Send acknowledgement to client that the client was either accepted or rejected0 i) {" P# |. x3 j; m/ V
ServerAckVersionPacket packet( pPlayerData->bAllow, dwFrom );
8 D9 r* y6 j- Q k SendPacket( dwFrom, &packet, sizeof(packet), TRUE, 0 );
3 |$ k# D$ g5 M}</P>
# o& Z9 L/ n# t. x( P
+ h- ]5 [; ~; p& ?7 M<P>: N* a7 |* v6 A' \! j
//-----------------------------------------------------------------------------! f1 }( q, r6 N- L0 h) h
// Name:
: Y" A# H! l) |3 j& `// Desc:
/ W* U( G, g3 k8 o) w8 B7 n//-----------------------------------------------------------------------------
% U5 z+ d( J: }$ m3 y) z; l1 a* `BOOL CMazeServer::IsClientVersionSupported( DWORD dwClientVersion )
% n. `+ e6 Z7 r( w/ X6 U{
; }3 [, ]7 H$ p4 `0 v switch( dwClientVersion )1 x- d2 R, }/ r
{
1 T( m1 c% D8 [/ v case 107: // only v107 is supported
K* O! {+ A- p% P7 L; D1 m return TRUE;
; B+ m7 n+ S: F- X, T+ q2 d default:, O( V# E& ^7 K
return FALSE;* X% F0 ~* {) k, K0 y
}
. ~& }( g( B& ]. B6 t' v9 L}</P>6 B( q8 @( m" _: |* C
% ^8 ]- m7 i+ I" L
<P>+ j' `; C p; W) |4 `) U! s% J
//-----------------------------------------------------------------------------) v; N' d2 T* _
// Name: ! b: X) D9 d1 w) q& a
// Desc:
" s8 G$ Z0 @' F//-----------------------------------------------------------------------------
3 U& q; K$ D* E# F5 hvoid CMazeServer::HandleUnknownPacket( DWORD dwFrom, ClientPacket* pClientPack, DWORD size )2 y9 P5 p9 V, \" \/ D" Z
{; [ ^. z2 t# r. u0 _" [1 N
if( m_dwLogLevel > 1 )
+ N1 A& e! F6 v% c# H9 U# K$ s: [+ s: ^ ConsolePrintf( SLINE_LOG, TEXT("ERROR! Unknown %d byte packet from player %0.8x"), size, dwFrom );</P>3 n" f6 V1 d. F$ I0 J- b
<P> m_pNet->RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
3 E8 j- j" Z4 ]}</P>
1 d% T3 f0 \9 O; Z, q! x( E+ K: E- {/ X+ x" c5 Q
<P>
: s/ z) R. ~" W* `//-----------------------------------------------------------------------------
6 ?8 Y# {# s2 ?% q8 ?/ g// Name:
2 V9 Y4 v4 W3 R" z! C// Desc:
4 [. e: X8 v D$ v//-----------------------------------------------------------------------------
) H# e |9 {7 ODWORD CMazeServer::IDHash( DWORD id )
; y0 {3 d( {- o{1 K- Y+ M" m/ |4 m+ G/ i
DWORD hash = ((id) + (id>>8) + (id>>16) + (id>>24)) & (NUM_ID_HASH_BUCKETS-1);
) J. _* |+ y! E+ W: |$ z# y; r! R1 K' a return hash;
# w" w: x& T5 X9 j& z; q}</P>
B8 b8 o& a* D) A! f+ z3 |7 ?# F- P; a* K$ S
<P>. s0 u9 a) K: d4 M8 j4 E
//-----------------------------------------------------------------------------; C5 E" j: `8 {$ J
// Name:
n3 f, a& H1 a7 r// Desc:
; o2 }7 @' |( o# @//-----------------------------------------------------------------------------
5 T1 [' ?) m7 B# Q7 evoid CMazeServer::RemovePlayerDataID( PlayerData* pPlayerData )* Q/ f6 {/ O% q! O1 u! \- r
{
: n' s' ^2 \ A // Hash the ID to a bucket number2 }% z2 O- E0 Y9 G
DWORD bucket = IDHash( pPlayerData->NetID );</P>0 g* S6 |) M/ n. T5 ]6 o
<P> // Lock that hash bucket$ M! y; s& _/ o& g E Y# S2 o
const DWORD buckets_per_lock = NUM_ID_HASH_BUCKETS / NUM_ID_HASH_BUCKET_LOCKS;' Z8 g( S2 j; n1 F
m_IDHashBucketLocks[bucket/buckets_per_lock].Enter();</P>7 G8 y# D7 k I7 Y7 ~
<P> // Loop though players in bucket until we find the right one
5 z* t3 \0 y* Z6 Y* \' z; c7 @4 r: a! V) n PlayerData* pPt = m_pstIDHashBucket[bucket];0 H1 ~8 Y9 w: N* V. C# t
PlayerData* pPrev = NULL;* o$ L6 a$ \% |% \1 H
while( pPt )5 e$ C' ]! ?) ?4 i; K) m+ f
{
+ N o. h) l! ]2 N1 ]/ R. G if( pPt == pPlayerData ) @1 A! ]5 ?/ f D# q
break;
. P, F) A: s' D- _; X- F2 ]+ ~ pPrev = pPt;
# S0 \+ G# L: b7 B1 b pPt = pPt->pNextInIDHashBucket;
- o) ~5 q1 j4 B$ j$ T, Y$ _ }</P>& U( l* ?/ z% U/ a
<P> if( pPt )
+ Q& q0 d) y; X& h0 _ {" K4 j- p; P4 E% b6 H
if( pPrev )
- S$ G; e& A& ~2 ]: J6 m pPrev->pNextInIDHashBucket = pPt->pNextInIDHashBucket;0 p+ l- j7 a k7 I k3 |. F, }
else/ k; S" y/ D5 ?7 A$ z5 k
m_pstIDHashBucket[bucket] = pPt->pNextInIDHashBucket;% K2 @" |2 R' d) S6 z: d
pPt->pNextInIDHashBucket = NULL;4 j! S5 ?2 H: x: c
}</P>$ {: x4 E$ H- B) {
<P> // Unlock the hash bucket; g$ C% Y7 ^7 {# q3 Y
m_IDHashBucketLocks[bucket/buckets_per_lock].Leave();
! S- G3 }8 k! }# N* A8 R' n6 N& k}</P>& F) r/ ^5 v) `
. ?& P+ T, e3 B+ v
<P>) E0 j; z. E6 S% p( U
//-----------------------------------------------------------------------------
9 @8 d4 H3 k* f2 k/ l% }// Name: * U+ q& ]& X% |2 d; F
// Desc: ) S6 p+ z; t3 T3 b2 e4 `. k9 ]
//----------------------------------------------------------------------------- @2 u- T4 ^: g3 f N
void CMazeServer::SetPlayerDataForID( DWORD id, PlayerData* pPlayerData )
- h" B# T" l/ i7 ^! _+ b5 c{; u4 ^* e% Y6 o
// Make sure this player isn't added twice to the m_pstIDHashBucket[], H y( \5 f) A* s) H
// otherwise there will be a circular reference
& m- S1 [1 T+ a) d PlayerData* pSearch = GetPlayerDataForID( id );
' Z" R+ z3 d! N5 t! T6 `( J' s if( pSearch != NULL )
F# e3 t' x8 E5 n- ~- h: d2 F d; B return;</P>
: ~& L7 ] O& ~' ]+ T$ B<P> // Hash the ID to a bucket number
- C& ^& ~6 _+ R* d+ H DWORD bucket = IDHash( id );</P>
. K& P k0 {/ T<P> // Lock that hash bucket
& D$ B8 R4 S2 z2 |! Z const DWORD buckets_per_lock = NUM_ID_HASH_BUCKETS / NUM_ID_HASH_BUCKET_LOCKS;, l' w: o) m F. _6 w2 t
m_IDHashBucketLocks[bucket/buckets_per_lock].Enter();</P>8 G2 o# l. d1 n( ^
<P> // Add player onto hash bucket chain
- }& @" n% I( G' J) j- T7 c pPlayerData->pNextInIDHashBucket = m_pstIDHashBucket[bucket];
7 [4 R3 m, U# R0 g) C0 k m_pstIDHashBucket[bucket] = pPlayerData;</P>
`$ Y' R! ~# r) I( y* \( R<P> // Store net id in player; r' e( L$ n6 K" M8 `: a5 t8 A
pPlayerData->NetID = id;</P> Z1 I( [0 L! r4 u _) m0 \( x" [
<P> // Unlock the hash bucket _6 m( u8 Y$ a9 d' _+ J. t
m_IDHashBucketLocks[bucket/buckets_per_lock].Leave();
2 @# e0 s x& y) H- ^, E8 l}</P>) F6 u$ f5 S$ ]3 A: \2 ~
W' P/ k k- p( {- N<P>/ r8 p, A- g/ T
//-----------------------------------------------------------------------------
) G9 R, |3 L* r8 o// Name:
# q4 u, B. A* u* ~" h. Q// Desc:
0 h" j4 _7 M3 f; N/ B# B//-----------------------------------------------------------------------------: p/ n) }% K* j8 I4 E
PlayerData* CMazeServer::GetPlayerDataForID( DWORD id )
/ u! E$ T( Z3 n, I9 |{+ C* H# ]" t. @# ^
// Hash the ID to a bucket number
/ o& Q& n6 ~2 P" E# Y1 p DWORD bucket = IDHash( id );</P>
( ]- h9 l' | }3 @( O$ \<P> // Lock that hash bucket& Q; I4 A+ P$ e2 e* c
const DWORD buckets_per_lock = NUM_ID_HASH_BUCKETS / NUM_ID_HASH_BUCKET_LOCKS;0 j) Y( t6 _+ I' Z' w9 S
m_IDHashBucketLocks[bucket/buckets_per_lock].Enter();</P>2 o$ ]6 ~4 r3 b: t4 M
<P> // Loop though players in bucket until we find the right one
3 m! C( }! j) F B PlayerData* pPlayerData = m_pstIDHashBucket[bucket];
7 \+ v+ C" t$ c" o, i+ v3 `, y while ( pPlayerData )
- S. M3 X! m2 {1 E$ V" B; K$ j {
- h5 o& C; A. s; T: [4 E4 p2 v if( pPlayerData->NetID == id )$ ^7 H M8 b+ u, Q) [1 G1 |
break;
6 Z8 o3 ^6 W2 I2 D( R; x( y4 W9 w. s pPlayerData = pPlayerData->pNextInIDHashBucket;1 K3 m8 Y' A! K5 A& w
}</P>
7 p( t3 A4 k9 ~) M5 D5 d; @9 p<P> // Unlock the hash bucket
7 A' @* [* b/ L& G5 J! F m_IDHashBucketLocks[bucket/buckets_per_lock].Leave();</P>
s( {3 z* }: R7 N6 V2 C3 S<P> // Return the player we found (will be NULL if we couldn't find it)
% t- z: z- _: j7 ?; B' q return pPlayerData;
( N( p) v; Y. B+ f( i( n0 R}</P>
6 `; |1 m, S- R! b4 a: T
; N5 f3 |6 e3 B% U9 `+ q<P>
5 d6 {2 N1 m# q( U6 j$ V& P: |//-----------------------------------------------------------------------------
\' l/ m% u* K! J `8 ^: r// Name:
: l1 F0 t( \8 e" h// Desc: calls DisplayConnectionInfo for each connection in a round-robin manner/ Q( `3 k' V1 v/ m4 p
//-----------------------------------------------------------------------------
3 `: h1 x% Y) `void CMazeServer: isplayNextConnectionInfo()! G. ?4 n6 }5 a/ ], F9 l
{/ ~7 O1 n& W0 L9 P/ V
if( m_pNet )
4 r. \9 r0 g/ C* ^3 v9 f6 S {! J% Q( P4 f8 X& |
// Find the player that was displayed the longest time ago, and display it.! e# |& @1 z0 c
FLOAT fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
, m9 {! O b8 S- W/ X PlayerData* pOldestPlayerData = NULL;( j6 i# E/ @3 u
FLOAT fOldestTime = 0.0f;</P>5 I$ \9 Y$ }& |
<P> m_PlayerDataListLock.Enter();</P>
( }" C! n/ L7 M Y2 G1 Z<P> PlayerData* pPlayerData = m_pFirstActivePlayerData;" T2 |" k- e; i, h- ]6 B: y
while ( pPlayerData )) g5 b- V8 G2 t7 G1 ~
{
' O5 q+ l. K+ J$ L if( fCurTime - pPlayerData->fLastCITime > fOldestTime )3 N. j7 [" U. q6 K. C9 ~
{% \# Y2 v! W: L2 l: o
fOldestTime = fCurTime - pPlayerData->fLastCITime;
" L2 @' L! [& T. Z3 [8 t6 T! z pOldestPlayerData = pPlayerData;3 G; I7 E% F' h
}</P>7 Y% E* N$ N4 z8 }6 R
<P> pPlayerData = pPlayerData->pNext;- K9 \. M6 ~5 C. ^3 |/ q
}</P>7 x# f% v- U5 }8 r0 b. F
<P> // Display the player with the oldest CI field, and update its CI field.3 W, H; d8 N; m \
if( pOldestPlayerData )
6 ]/ w$ h4 O+ I& e {/ J6 a- C: x4 }4 m. O* v) @6 W
ConsolePrintf( SLINE_LOG, TEXT("Displaying connection info for next player") );: n) Z/ ?- A$ e/ v8 {) k7 t; \
DisplayConnectionInfo( pOldestPlayerData->NetID );
7 ?! h' ]. d1 T8 i pOldestPlayerData->fLastCITime = fCurTime;
# K+ n' U. p. _1 \ R$ e }- c; l ]# R0 n% b3 g/ Z* ~* L
else% c& l. a W4 J1 @ U' I& H( F4 N9 l
{9 L3 v' k4 h0 R g9 t
ConsolePrintf( SLINE_LOG, TEXT("No players found") );
2 K6 B9 \. P' I0 P }</P>
+ j: w. X7 h( r9 R7 h+ w<P> m_PlayerDataListLock.Leave();. d* Z4 Y, R5 @, f- l" X0 f$ ?
}6 z7 S3 K5 O {8 }8 I, n
}</P>
, L" U1 v' Z5 S, |8 y
+ I% ?' \# y/ |, c7 r5 v<P>
$ F" [0 v7 D9 R! N a//-----------------------------------------------------------------------------
2 l' r {$ l+ I4 E9 }// Name: - @8 t) m. [( n0 P) W& V
// Desc: / k1 F5 O! X$ B6 U. E
//-----------------------------------------------------------------------------8 a& p8 z8 ?) Z
void CMazeServer: rintStats()* O6 D9 J* J" J. Z2 `
{4 ^' Y8 }5 p8 i3 O: ^- P1 K7 Z
ConsolePrintf( SLINE_LOG, TEXT("Thread count: Active=%d Avg=%.2f Max=%d"),
0 T0 r4 Z; T% B m_wActiveThreadCount, m_fAvgThreadCount, m_wMaxThreadCount );$ j5 A6 e) R# z. \6 H& ]7 X* s" E
ConsolePrintf( SLINE_LOG, TEXT("Thread Time: Avg=%.4f Max=%.4f(s)"),- P; i+ b! x/ p. b4 C9 H+ k$ j0 L
m_fAvgThreadTime, m_fMaxThreadTime );
; G8 X1 C. _3 `% D: _: b' Z3 x ConsolePrintf( SLINE_LOG, TEXT("Players online (not including server player): %d"), m_dwPlayerCount );
$ A X9 i& e- I5 V$ p6 P3 N ConsolePrintf( SLINE_LOG, TEXT("Peak player count: %d"), m_dwPeakPlayerCount );* c8 F- q! l: Z g8 A C8 B
}</P>+ r/ p6 S- ?) N3 P: O( @% R
) _& B6 Y8 f: F. E7 b1 s<P>
/ h* ]* b4 k3 b0 w$ k//-----------------------------------------------------------------------------& o- u- U7 x5 l3 S6 j0 k I
// Name:
; ]1 w: H9 P/ I% L: O5 L. c& U// Desc:
% ^1 ?( n. G$ f* A3 ~8 Y//-----------------------------------------------------------------------------) E0 c; V1 `+ F* h0 c- Z
void CMazeServer: isplayConnectionInfo( DWORD dwID )
% ^4 o! d9 r' o+ H/ V5 s{
3 y7 B" C$ f% O0 J) A( l( p TCHAR strInfo[5000];9 E' H4 F) \( o& Y9 H
TCHAR* strEndOfLine;/ ^4 x) K/ f2 n8 c# l
TCHAR* strStartOfLine;</P>
$ e5 u4 X' a7 b' r<P> // Query the IOutboudNet for info about the connection to this user
2 W8 Y' A. N2 \4 t+ P5 G4 D m_pNet->GetConnectionInfo( dwID, strInfo );</P>- |3 H- a0 D3 t+ I0 V% ^* M+ j7 V" ^
<P> ConsolePrintf( SLINE_LOG, TEXT("Displaying connection info for %0.8x"), dwID );; V2 }; [& H7 N0 l$ d
ConsolePrintf( SLINE_LOG, TEXT("(Key: G=Guaranteed NG=Non-Guaranteed B=Bytes P=Packets)") );</P>
% l, |: W% |) u<P> // Display each line seperately E; q9 @$ M4 s, Z
strStartOfLine = strInfo;8 [2 M5 \ {5 ^ P. w/ T* H
while( TRUE )
. s0 n0 C# w* b. K {! u+ M J' i* \( w
strEndOfLine = _tcschr( strStartOfLine, '\n' );
9 Q' [% {" ^+ d' l5 A) c( T: W if( strEndOfLine == NULL )* F8 h- o# V+ [& d* A
break;</P>& m7 N( J, r9 N# d- ^/ I% W; O& `
<P> *strEndOfLine = 0;
* h, ] K' w* p2 F ConsolePrintf( SLINE_LOG, strStartOfLine );5 ~0 \/ c9 }2 y3 v3 ^' c
strStartOfLine = strEndOfLine + 1;
+ p2 n# q) X% A- Z I% ?* w9 Y }
7 [7 E* b/ z- ~/ O}</P>% p4 d- [+ b: [. H2 D2 z( |# u
5 j/ T% V# J U. K, H8 w/ Q
<P>) s, ^7 U1 H9 o+ b6 v9 A# \
//-----------------------------------------------------------------------------6 z5 v; S. [: U+ t% e: M
// Name:
4 ?) K7 _6 H* _& g5 D// Desc:
. E6 ~" B ^3 j/ {& _- R) ?: J//-----------------------------------------------------------------------------
: ^* P! i7 y9 u) _$ S+ o3 NHRESULT CMazeServer::SendPacket( DWORD to, void* pData, 7 n* u/ R5 a3 h. P% L
DWORD size, BOOL reliable, DWORD dwTimeout ): P8 G' ? ~) H% V
{
- Z5 f/ W4 m9 b+ U' s) f$ f // Chance of forcing any packet to be delivered reliably; t. T# Y# |: R! G
if( m_Rand.Get( 100 ) < m_dwServerReliableRate )+ w. H9 C5 [3 z% G! e
reliable = TRUE;</P>
) s: j% \2 N+ M; |8 q9 X# V<P> return m_pNet->SendPacket( to, pData, size, reliable, dwTimeout );
. U/ q5 G2 j' }0 ?}</P>2 p6 \% j1 P5 u1 |2 U
; i& z1 c0 C1 Q) K& A) N$ N- A6 o
<P>
4 Q; G5 r, F; y( l) B4 b8 C//-----------------------------------------------------------------------------
1 s8 v8 G- B* A; ^' D" R// Name:
% H! r' I3 x* Z: r9 y( ^// Desc: ) l2 h& e3 [( c1 I: N$ G( ^) k
//-----------------------------------------------------------------------------$ A Z. G9 C7 e& p3 i. @( K
void CMazeServer::SendConfigPacketToAll( ServerConfigPacket* pPacket )
. L* H- a. x/ D$ g5 h{
" t" I2 r- M% o3 c1 d# n. l" Z // If we're up and running, then send this new information to all clients) @4 ]3 k' t5 I' M8 `9 M
if( m_pNet )% E, I. l1 w1 c; M" k
{
5 s: k9 k& ` i //Use the AllPlayers ID: v9 \# R- {( M3 G3 w8 K8 y8 Y
SendPacket( DPNID_ALL_PLAYERS_GROUP, pPacket, sizeof(ServerConfigPacket), TRUE, 0 );* L, R& `0 i K! R3 m! ~ w
}
" ~, I" ^5 m: w( d}</P>
8 X: |- m' O9 M* W0 }. H
$ n% [' a/ C7 H<P>
1 [% |. |; ~, f# S2 ~& B+ s1 x//-----------------------------------------------------------------------------
q _1 Z8 \% \1 N// Name: 9 y, p3 \1 V# C! }6 P. T( N+ H5 Z0 i
// Desc: 7 }6 a: C+ X5 g" \$ M
//-----------------------------------------------------------------------------
( ?) O) O7 O+ X9 t2 e9 F- ~9 Avoid CMazeServer::SetClientReliableRate( DWORD percent )
i' A+ w. a( }3 R{
8 q( r2 u" O/ p // Update client config, and build packet containing that data' r4 {1 J8 _3 _# f, Y1 Q* n: q
m_ClientNetConfigLock.Enter();
! k2 o0 U9 a: V m_ClientNetConfig.ubReliableRate = BYTE(percent);
+ N2 B# \9 m0 }& A ServerConfigPacket packet( m_ClientNetConfig );
8 d3 `. O, r! d# q& U7 D1 w m_ClientNetConfigLock.Leave();</P>- ?9 ^, F% c- i6 p: f
<P> SendConfigPacketToAll( &packet );
! `& u1 S) l& A6 W}</P>( s5 ?$ `1 e1 t5 t! b) D
1 s7 L3 y# V) I( q6 p
<P>% _2 |7 I L+ G2 T$ q$ L
//-----------------------------------------------------------------------------6 ?: m, O' U( b2 K1 w; ^/ f
// Name: / t8 D4 j4 T( K: i& a
// Desc:
$ N N( C, w. t, Q/ K( w. O R//-----------------------------------------------------------------------------
- V% n5 M0 `; j' o, m- Fvoid CMazeServer::SetClientUpdateRate( DWORD rate )( X* ~1 w7 I6 H& t
{4 y1 h* E8 {4 V) u9 r# D
// Update client config, and build packet containing that data
% v% Z4 q! ^4 M5 I: C m_ClientNetConfigLock.Enter();* k* o+ x1 D4 M5 R2 j- r
m_ClientNetConfig.wUpdateRate = WORD(rate);+ F N1 Q: \( k4 D* n' M" ^ }9 `
ServerConfigPacket packet( m_ClientNetConfig );9 q5 e u/ `* F" L( r5 E: Y8 l
m_ClientNetConfigLock.Leave();</P>
j( L4 y* V! [8 q, B7 P<P> SendConfigPacketToAll( &packet );
9 f9 p1 |" U1 K}</P>! `, `7 Q8 Y2 v. B+ Z) X: e* d7 F: A
0 H! L) ~1 i2 J5 _( f: r9 `<P>& _7 ]/ l0 r+ p @# s
//-----------------------------------------------------------------------------9 Q3 i: v+ Z F; m5 p, U1 l
// Name:
' N2 o. z5 ^& @! Z' T// Desc: 4 u# U- I) r, J: E# k
//-----------------------------------------------------------------------------
& F3 M2 t; S# \void CMazeServer::SetClientTimeout( DWORD timeout )
1 F9 R* u6 d" z7 Q L{
, @% [6 g$ K) u& Z* B8 X // Update client config, and build packet containing that data) r2 D. n$ N+ ]2 F; N
m_ClientNetConfigLock.Enter();
6 M4 u2 C& ?) N m_ClientNetConfig.wTimeout = WORD(timeout);! H- R9 D$ o1 ^/ k
ServerConfigPacket packet( m_ClientNetConfig );
! A5 c" H+ C" t. {0 D m_ClientNetConfigLock.Leave();</P>
* p4 K3 [ _, `1 M% v% L3 Q<P> SendConfigPacketToAll( &packet );
% {0 u6 X5 r/ W4 X; b}</P>
% @2 Q1 Q" a% {$ e6 m) a& X
: t6 E e$ R( S5 Z( [' h<P>, {, U- l9 z7 z8 i
//-----------------------------------------------------------------------------4 I! M5 a3 |( L
// Name:
# H( i% D' a. m3 q! h3 h }// Desc: & g4 u9 P( n8 h6 c3 E; g9 S' P9 t+ Z
//-----------------------------------------------------------------------------
$ S9 t P& d: u% q9 r; Gvoid CMazeServer::SetClientPackSize( DWORD size )
! N% J' f0 r0 a) q6 u8 |7 I{- x, C7 }! r7 h9 _, J* `/ p
// Update client config, and build packet containing that data A2 w! ^4 C, t) O( Q, j
m_ClientNetConfigLock.Enter();
5 c0 ]; [: Z6 f1 W 3 B$ d: S' E, g3 z5 E+ ^
m_ClientNetConfig.ubClientPackIndex++; //Increase index and verify location in array.
' m+ l* J/ P+ j: }: v- b) S. i if(m_ClientNetConfig.ubClientPackIndex >= PACK_ARRAY_SIZE) * O8 \6 S( a' Z" E
m_ClientNetConfig.ubClientPackIndex = 0;</P>/ O+ n- `/ t, A
<P> m_ClientNetConfig.wClientPackSizeArray[m_ClientNetConfig.ubClientPackIndex] = WORD(size);7 c3 S5 G& ~, R, L& c0 e3 R5 @+ {
ServerConfigPacket packet( m_ClientNetConfig ); h4 I, K& k* O) z( o( Y9 J
m_ClientNetConfigLock.Leave();</P>
5 T$ |! a& V8 q5 t, q7 Q0 Y5 x<P> SendConfigPacketToAll( &packet );! P, M* B5 `$ Q* c: U5 a4 ^
}</P>5 c" Z1 Z+ `! y7 W3 ~) s% S D
1 E; b! X3 I5 U4 J0 }) }
<P>
H2 M4 {1 x& e1 x//-----------------------------------------------------------------------------( I9 N0 z. X$ H6 V4 p% f3 t
// Name:
0 t3 b9 G( N1 _9 C5 }& K3 l' I/ Y// Desc: 6 m2 x( K" u$ R, i% F7 t5 A
//-----------------------------------------------------------------------------
, S7 U3 h/ `: z' cvoid CMazeServer::SetServerPackSize( DWORD size )
+ N) h* k8 p$ r{5 v) {1 w d% y4 |6 r$ a
// Update client config, and build packet containing that data* B* e; C0 X4 a7 _ q
m_ClientNetConfigLock.Enter();
% }7 [; `: t) H! T# `: |6 l/ @4 r , o1 j9 M& ]3 W! O, a: C; n
m_ClientNetConfig.ubServerPackIndex++; //Increase index and verify location in array.
% C- a# x0 R; @& \5 y if(m_ClientNetConfig.ubServerPackIndex >= PACK_ARRAY_SIZE)
6 H4 X k3 Q, K. B1 K: Y8 G3 U m_ClientNetConfig.ubServerPackIndex = 0;</P>
3 X$ l" I) a' {) A) g! ]7 {( y<P> m_ClientNetConfig.wServerPackSizeArray[m_ClientNetConfig.ubServerPackIndex] = WORD(size);, @4 b6 P9 D7 I* c6 Y: W4 ~
ServerConfigPacket packet( m_ClientNetConfig );
! _2 J+ U. {7 s4 ~: \( t$ E m_ClientNetConfigLock.Leave();</P>
6 J0 _7 W! G2 @2 H) O, J<P> SendConfigPacketToAll( &packet );0 X+ u- F6 k- A4 _" f* p
}</P>4 [, q# U3 J# r9 y7 X
<P>
. N1 V- g3 E/ Q& b) @# p! t//-----------------------------------------------------------------------------
, N& p4 b( g M* H! }// Name: ! b- f- P' l( r+ [0 D+ E! L
// Desc: 8 N2 a- u# V4 ^8 @! i' A0 ~+ [' @
//-----------------------------------------------------------------------------$ C/ v* B% O5 E* ? b+ r6 S% H, M
void CMazeServer::SetClientThreadWait( DWORD dwThreadWait )
/ y7 V/ U2 y9 @, u5 j* W{
$ Y4 ^. [4 x& q7 q) Y' E" J" y // Update client config, and build packet containing that data
" w. m3 _# a T) J4 X m_ClientNetConfigLock.Enter();
1 j, Q' T+ D! ^3 _ 4 M8 e) m) F9 _- f, v& `2 p
m_ClientNetConfig.dwThreadWait = dwThreadWait;+ S0 s$ N0 r( }: S
ServerConfigPacket packet( m_ClientNetConfig );7 Q2 O: b* h/ O& H
m_ClientNetConfigLock.Leave();</P> j* h5 i0 {1 \) b, W4 _, ^( o
<P> SendConfigPacketToAll( &packet );
: c4 A* T' ~/ P2 N1 t2 }- k0 J* {}</P></DIV> |
zan
|