//----------------------------------------------------------------------------
// File: mazeserver.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _MAZESERVER_H
#define _MAZESERVER_H





//-----------------------------------------------------------------------------
// Name: 
// Desc: 
//-----------------------------------------------------------------------------
#include "NetAbstract.h"
#include "SyncObjects.h"
#include "Maze.h"
#include "Random.h"
#include "Packets.h"
#include "Trig.h"

class   CMaze;
struct  ClientPosPacket;

#define SERVER_MAX_WIDTH    128
#define SERVER_MAX_HEIGHT   128
#define DEFAULT_MAZE_WIDTH  16
#define DEFAULT_MAZE_HEIGHT 16
#define DEFAULT_SEED        314159
#define LOCK_GRID_SIZE      16




//-----------------------------------------------------------------------------
// Name: 
// Desc: 
//-----------------------------------------------------------------------------
struct PlayerData
{
    DWORD           dwID;                   // Client ID
    DWORD           NetID;                  // NetID for owning player (0 if none)
    DWORD           dwVersion;              // Version of the owning player
    BOOL            bAllow;                 // If FALSE, then we should drop this player

    // Links for the various lists
    PlayerData*     pNext;                  // Free/active PlayerData list (double link)
    PlayerData*     pPrevious;
    PlayerData*     pNextInCell;            // Cell list (single link)
    PlayerData*     pNextInIDHashBucket;    // ID hash bucket (single link)

    FLOAT           fLastDisplayTime;
    FLOAT           fLastCITime;
    BOOL            bActive;

    float           fPosX;                  // Floating point position
    float           fPosY;

    float           fVelPos;                // Floating point velocity

    WORD            wCellX;                 // Coordinates of the cell this player is in
    WORD            wCellY;                 // or (0xffff,0xffff) for off-map
    ANGLE           aYaw;
    INT             nVelYaw;                // Signed velocity of current rotation

    DWORD           dwNumNearbyPlayers;     // Number of nearby players

    DWORD           pad[4];
};




//-----------------------------------------------------------------------------
// Name: 
// Desc: 
//-----------------------------------------------------------------------------
struct ServerCell
{
    PlayerData* pFirstPlayerData;
};




//-----------------------------------------------------------------------------
// Name: 
// Desc: 
//-----------------------------------------------------------------------------
class CMazeServer : public INetServer
{
public:
    CMazeServer();

    // From INetServer
    void    OnAddConnection( DWORD dwID );
    HRESULT OnPacket( DWORD from, void* pData, DWORD dwSize );
    void    OnRemoveConnection( DWORD dwID );
    void    OnSessionLost( DWORD dwReason );

    // Hook up to the network service
    void    SetOutboundServer( IOutboundServer* poutnet ) { m_pNet = poutnet; };

    // Initialisation - need to hook up a maze object
    HRESULT Init( BOOL bLocalLoopback, const CMaze* pmaze );
    void    Shutdown();

    // Chance of server sending packets via reliable transport
    void    SetServerReliableRate( DWORD percent ) { m_dwServerReliableRate = percent; };
    DWORD   GetServerReliableRate() const { return m_dwServerReliableRate; };

    // Timeout of server's packets
    void    SetServerTimeout( DWORD timeout ) { m_dwServerTimeout = timeout; };
    DWORD   GetServerTimeout() const { return m_dwServerTimeout; };

    // Change of client sending packets via reliable transport. Setting this causes the server
    // to propagate this setting to all currently connected clients
    void    SetClientReliableRate( DWORD percent );
    DWORD   GetClientReliableRate() const { return DWORD(m_ClientNetConfig.ubReliableRate); };

    // Change client update rate. Setting this causes the server to propagate this setting to all
    // currently connected clients
    void    SetClientUpdateRate( DWORD rate );
    DWORD   GetClientUpdateRate() const { return DWORD(m_ClientNetConfig.wUpdateRate); };

    // Change client timeout. Setting this causes the server to propagate this setting to all
    // currently connected clients
    void    SetClientTimeout( DWORD timeout );
    DWORD   GetClientTimeout() const { return DWORD(m_ClientNetConfig.wTimeout); };

    // Change Client position pack size. Setting this causes the server to propagate this setting to all
    // currently connected clients
    void    SetClientPackSize( DWORD size );
    DWORD   GetClientPackSize() const { return DWORD(m_ClientNetConfig.wClientPackSizeArray[m_ClientNetConfig.ubClientPackIndex]); };

    // Change Server position pack size.
    void    SetServerPackSize( DWORD size );
    DWORD   GetServerPackSize() const { return DWORD(m_ClientNetConfig.wServerPackSizeArray[m_ClientNetConfig.ubServerPackIndex]); };
    
    // How long the user wants to hold the Server's Dplay Threads
    void    SetServerThreadWait( DWORD threadwait ) { m_dwServerThreadWait = threadwait; };
    DWORD   GetServerThreadWait() const { return m_dwServerThreadWait; };

    // How long the user wants to hold the Server's Dplay Threads
    void    SetClientThreadWait( DWORD threadwait );
    DWORD   GetClientThreadWait() const { return DWORD(m_ClientNetConfig.dwThreadWait); };
    
    
    // Various commands
    void    DisplayConnectionInfo( DWORD dwID );
    void    DisplayNextConnectionInfo();
    void    PrintStats();

    void    SetLogLevel( DWORD dwLogLevel ) { m_dwLogLevel = dwLogLevel; }
    DWORD   GetLogLevel() { return m_dwLogLevel; }

    DWORD   GetNumPlayers() { return m_dwPlayerCount; }

protected:
    BOOL                m_bLocalLoopback;
    IOutboundServer*    m_pNet;
    const CMaze*        m_pMaze;
    DWORD               m_dwLogLevel;
    DWORD               m_dwWidth;
    DWORD               m_dwHeight;
    CCriticalSection    m_AddRemoveLock;

    // A fixed sized grid of locks which we lay over the maze to control access to it
    // We demand that the maze dimensions are a power-of-2 times the dimensions of this
    // grid, and pre-store that power to allow fast translation
    CLockArray<LOCK_GRID_SIZE,LOCK_GRID_SIZE>   m_LockGrid;
    DWORD               m_dwMazeXShift;
    DWORD               m_dwMazeYShift;
    void                LockCell( DWORD x , DWORD y );
    void                UnlockCell( DWORD x , DWORD y );
    void                LockRange( DWORD x1 , DWORD y1 , DWORD x2 , DWORD y2 );
    void                UnlockRange( DWORD x1 , DWORD y1 , DWORD x2 , DWORD y2 );
    void                LockCellPair( DWORD x1 , DWORD y1 , DWORD x2 , DWORD y2 );
    void                UnlockCellPair( DWORD x1 , DWORD y1 , DWORD x2 , DWORD y2 );
    CCriticalSection    m_OffMapLock;

    // The PlayerData lists
    PlayerData          m_PlayerDatas[MAX_PLAYER_OBJECTS];
    PlayerData*         m_pFirstActivePlayerData;
    PlayerData*         m_pFirstFreePlayerData;
    DWORD               m_dwActivePlayerDataCount;
    DWORD               m_dwPlayerDataUniqueValue;
    CCriticalSection    m_PlayerDataListLock;

    // The player object locks
    enum { NUM_PLAYER_OBJECT_LOCKS = 16 };
    CCriticalSection    m_PlayerDataLocks[NUM_PLAYER_OBJECT_LOCKS];
    void                LockPlayerData( PlayerData* pPlayerData ) { m_PlayerDataLocks[((pPlayerData-m_PlayerDatas) & (NUM_PLAYER_OBJECT_LOCKS-1))].Enter(); };
    void                UnlockPlayerData( PlayerData* pPlayerData ) { m_PlayerDataLocks[((pPlayerData-m_PlayerDatas) & (NUM_PLAYER_OBJECT_LOCKS-1))].Leave(); };

    PlayerData*        CreatePlayerData();
    void                DestroyPlayerData( PlayerData* pPlayerData );

    // The cell array and the "off-map" cell.
    ServerCell          m_OffMapCell;
    ServerCell          m_Cells[SERVER_MAX_WIDTH][SERVER_MAX_HEIGHT];

    // Remove playerdata from its cell
    void    RemovePlayerDataFromCell( PlayerData* pPlayerData );

    // Unsafe versions of add/remove. Must have playerdata and cell locked when you call this
    void    UnsafeRemovePlayerDataFromCell( PlayerData* pPlayerData );
    void    UnsafeAddPlayerDataToCell( PlayerData* pPlayerData );

    ServerCell* GetCell( PlayerData* pPlayerData )
    {
        if ( pPlayerData->wCellX == 0xffff )
            return &m_OffMapCell;
        else
            return &m_Cells[pPlayerData->wCellY][pPlayerData->wCellX];
    };

    void    HandleClientPosPacket( DWORD dwFrom, ClientPosPacket* pPacket );
    void    HandleClientVersionPacket( DWORD dwFrom, ClientVersionPacket* pClientVersionPack );
    void    HandleUnknownPacket( DWORD dwFrom, ClientPacket* pClientPack, DWORD size );

    BOOL    IsValidPackSize( DWORD wSize );
    BOOL    IsClientVersionSupported( DWORD dwClientVersion );

    DWORD   m_dwPlayerCount;
    
    CCriticalSection    m_csThreadCountLock;    
    WORD   m_wActiveThreadCount;
    WORD   m_wMaxThreadCount;
    FLOAT  m_fAvgThreadCount;
    FLOAT  m_fAvgThreadTime;
    FLOAT  m_fMaxThreadTime;

    DWORD  m_dwPeakPlayerCount;

    // Hashing DPIDs to PlayerData pointers
    void                SetPlayerDataForID( DWORD dwID, PlayerData* pPlayerData );
    PlayerData*        GetPlayerDataForID( DWORD dwID );
    void                RemovePlayerDataID( PlayerData* pPlayerData );
    DWORD               IDHash( DWORD dwID );
    enum { NUM_ID_HASH_BUCKETS = 1024 };
    enum { NUM_ID_HASH_BUCKET_LOCKS = 16 };
    PlayerData*        m_pstIDHashBucket[NUM_ID_HASH_BUCKETS];
    CCriticalSection    m_IDHashBucketLocks[NUM_ID_HASH_BUCKET_LOCKS];

    // Random number generator
    CRandom     m_Rand;

    // Send packet wrapper
    HRESULT SendPacket( DWORD dwTo, void* pData, DWORD dwSize, BOOL bReliable, DWORD dwTimeout );
    void SendConfigPacketToAll( ServerConfigPacket* pPacket );

    // Network configuration parameters
    DWORD               m_dwServerReliableRate;
    DWORD               m_dwServerTimeout;
    DWORD               m_dwServerThreadWait;

    ClientNetConfig     m_ClientNetConfig;
    CCriticalSection    m_ClientNetConfigLock;
};




//-----------------------------------------------------------------------------
// Name: 
// Desc: This function is called by the server to output informational text.
//       In the client it should probably just be a dummy function, in the server 
//       it should probably just spew out to the console
//-----------------------------------------------------------------------------
enum ServerBufferType { SLINE_PROMPT, SLINE_INPUT, SLINE_LOG, SLINE_CMD };
void ConsolePrintf( ServerBufferType enumLineType, const TCHAR* fmt , ... );



#endif
