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