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