Files
Client/Library/dxx8/samples/Multimedia/DirectPlay/DataRelay
LGram16 e067522598 Initial commit: ROW Client source code
Game client codebase including:
- CharacterActionControl: Character and creature management
- GlobalScript: Network, items, skills, quests, utilities
- RYLClient: Main client application with GUI and event handlers
- Engine: 3D rendering engine (RYLGL)
- MemoryManager: Custom memory allocation
- Library: Third-party dependencies (DirectX, boost, etc.)
- Tools: Development utilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 16:24:34 +09:00
..

//-----------------------------------------------------------------------------
// 
// Sample Name: DataRelay Sample
// 
// Copyright (c) 1999-2001 Microsoft Corporation. All rights reserved.
// 
//-----------------------------------------------------------------------------


Description
===========
  The DataRelay is similar to SimplePeer but differs by sending a single 
  target (or everyone) a packet of data with options specified in the 
  dialog's UI. It uses a worker thread to process received data, and 
  uses the ReturnBuffer() API so that no copying of the received buffers
  is done.

Path
====
  Source: DXSDK\Samples\Multimedia\DirectPlay\DataRelay 

  Executable: DXSDK\Samples\Multimedia\DirectPlay\Bin

User's Guide
============
  Host or connect to a session in the same manner as explained in SimplePeer.
  When the main dialog appears select the target, size, rate, and timeout values.
  Then click "Push to Send". This will send a packet of data to the target as
  the rate specified with the specified size.  Use the "Connection Info" dropdown 
  to specify a target to gather connection info on periodically.

Programming Notes
=================
  The DataRelay sample is very similar in form to the SimplePeer sample.  For 
  detailed programming notes on the basics this sample, refer to Programming 
  Notes section of the SimplePeer sample.

  The DataRelay differs by sending a single target (or everyone) a packet of 
  data with options specified in the dialog's UI. 

  When the "Push to Send" button is clicked, then win32 timer is created 
  that goes off every number of ms according to the UI.

  * Upon the WM_TIMER labeled TIMERID_NETWORK, it calls SendNetworkData().
	1. It creates a app defined struct, GAMEMSG_DATA_xxx on the heap.  
	   The struct derives from GAMEMSG_GENERIC.  GAMEMSG_GENERIC contains a 
	   packet type field so that the reciever and identify this app defined 
	   packet, and it contains a packet ID field.  This field is sequential 
	   and is displayed to the user whenever a packet is received.  The struct 
	   is created on the heap, since will use the DPNSEND_NOCOPY when calling 
	   SendTo() below.  The struct is filled with random data, however a real 
	   app would typically send player and world state data here. 	   
	2. It then creates a GAMEMSG_DATA_NODE which is then handed off to the
 	   app worker thread.  That thread will process the node, and
	   then update the UI to show that a packet was sent.  It is handed off
	   by adding the node to a linked list.  Since the worker thread also
	   accesses the linked list, we enter a critical section before adding the
	   node and leave it afterward.
	3. A DPN_BUFFER_DESC is then filled out passing in a pointer to the 
       app defined struct created above. 
	4. IDirectPlay8Peer::SendTo is called passing in the DPN_BUFFER_DESC, thereby
	   sending the app defined struct, GAMEMSG_DATA_xxx.  We call SendTo with
	   the flags DPNSEND_NOLOOPBACK | DPNSEND_NOCOPY.  DPNSEND_NOLOOPBACK tells
	   DirectPlay to not to send the buffer to us, and DPNSEND_NOCOPY means that
	   DirectPlay should not copy the buffer.  When the DPNSEND_NOCOPY is used,
	   the app itself owns the buffer, and the buffer must be on the heap.
	   When the DPN_MSGID_SEND_COMPLETE comes in, we will delete the buffer. 	    
	5. The event, g_hDPDataAvailEvent, is set telling the worker thread that 
	   there is data (a message to say that a packet was sent), is ready to 
	   be processed now.
	
  * Handle DirectPlay system messages.  See DirectPlayMessageHandler()
        The DataRelay handles the typical messages as described in the 
        SimplePeer programming notes, and in addition:
        
        - Upon DPN_MSGID_RECEIVE
            1. It casts the pReceiveMsg->pReceiveData to a GAMEMSG_GENERIC*.
            2. It then switches off the GAMEMSG_GENERIC's dwType. 
            3. If its a GAME_MSGID_GAMEPACKET, then it creates and fills out
               a GAMEMSG_DATA_NODE.  This node is then handed to a worker thread
               so it can be processed outside of the DirectPlay message handler.
               This is important since it keeps the DirectPlay threads working at
               full speed.
            4. After the node is added to the linked list using a critical section
               to lock, it returns DPNSUCCESS_PENDING.  This is important since 
               it tells DirectPlay that ownership of the buffer 
               has been transferred to the application, and so DirectPlay will 
               neither free nor modify it until ownership is returned 
               to DirectPlay through the ReturnBuffer() call.

        - Upon DPN_MSGID_SEND_COMPLETE
            1. It checks the pSendCompleteMsg->hResultCode for DPNERR_TIMEDOUT.
            2. If this occurs then it creates a new GAMEMSG_DATA_NODE with dwType
               set to DATA_TYPE_NETPACKET_TIMEOUT.  This will is passed to the 
               worker thread.  The worker thread will process this node, and 
               post a message to the dialog saying that the message timed out.
               A realistic application would want to take the appropriate steps here,
               such as resending new data or other measures.
            3. It deletes the buffer from the heap since we specified, DPNSEND_NOCOPY,
               so the buffer on the heap belows the app and it must clean it up. 
         
   * The worker thread.  See ProcessNetDataProc()
        - Upon the g_hDPDataAvailEvent
            1. When the event is signaled, then new data can be found in the 
               linked list, g_DataHead.  So it calls ProcessData().
            2. ProcessData() first enters the critical section, g_csDataList, so 
               that the other threads don't modify the linked list while it is 
               processing data.  It leaves this critical section at the end of the
               loop.  Typically better locking mechanisms would be used so that the 
               other threads (as well as the DirectPlay message handler threads) 
               aren't blocked while data is processed on this thread.               
            3. It runs through the linked list, processing each node.  For this
               simple sample all it does is posting a message to the dialog thread,
               containing a string for the dialog to display.
            4. After it is done processing the node, it calls 
               IDirectPlay8Peer::ReturnBuffer() so that DirectPlay can free
               buffer that it passed us in DPN_MSGID_RECEIVE.