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