全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:2686
推到 Plurk!
推到 Facebook!

請問...DirectPlay

尚未結案
xshado
一般會員


發表:2
回覆:4
積分:1
註冊:2002-07-24

發送簡訊給我
#1 引用回覆 回覆 發表時間:2002-12-07 21:30:28 IP:203.204.xxx.xxx 未訂閱
各位大大...小弟有一個DirectPlay的問題 小弟我最近的專題是要寫一個小遊戲ㄚ 要用DirectPlay作連線的功能 但是有關這方面的範例和說明很少,而且Microsoft SDK 又很複雜看不太懂....請問各位大大可以指點一下 如有簡單的範例程式是最好的.....謝謝啦..^^
RaynorPao
版主


發表:139
回覆:3622
積分:7025
註冊:2002-08-12

發送簡訊給我
#2 引用回覆 回覆 發表時間:2002-12-07 21:55:09 IP:61.221.xxx.xxx 未訂閱
引言: 要用DirectPlay作連線的功能 但是有關這方面的範例和說明很少,而且Microsoft SDK 又很複雜看不太懂....請問各位大大可以指點一下 如有簡單的範例程式是最好的.....謝謝啦..^^
xshado 你好: 如果你有安裝 Microsoft SDK 的話 在 ..\>>, > --
------
-- 若您已經得到滿意的答覆,請適時結案!! --
-- 欲知前世因,今生受者是;欲知來世果,今生做者是 --
-- 一切有為法,如夢幻泡影,如露亦如電,應作如是觀 --
xshado
一般會員


發表:2
回覆:4
積分:1
註冊:2002-07-24

發送簡訊給我
#3 引用回覆 回覆 發表時間:2002-12-12 16:57:37 IP:203.204.xxx.xxx 未訂閱
恩...很感謝您的指導        我有看了sample裡面的SimpleClientServer這個範例..           但是他是用TCP/IP來連的,請問可以用UDP來連嗎        有哪個範例適用UDP的ㄚ....謝謝囉..^^  
RaynorPao
版主


發表:139
回覆:3622
積分:7025
註冊:2002-08-12

發送簡訊給我
#4 引用回覆 回覆 發表時間:2002-12-12 19:08:50 IP:61.221.xxx.xxx 未訂閱
引言: 我有看了sample裡面的SimpleClientServer這個範例.. 但是他是用TCP/IP來連的,請問可以用UDP來連嗎 哪個範例適用UDP的ㄚ....謝謝囉..^^ < face="Verdana, Arial, Helvetica"> xhado 你好: 我看了一下你所說的 sample code 主要是利用 IDirectPlay8Address,IDirectPlay8Server, IDirectPlay8Client, IDirectPlay8LobbiedApplication..等 來達成目的~~其中 SetSP 可設定 Service Provider 在 ..\Microsoft SDK\Include\prerelease 目錄下 你可以查到 CLSID_DP8SP_TCPIP 的定義 另外~~比較相似的定義還有 CLSID_DP8SP_IPX, CLSID_DP8SP_MODEM, CLSID_DP8SP_SERIAL, 並沒有發現 CLSID_DP8SP_UDP 的定義 所以~~我猜喔~~可能是沒有吧 --
------
-- 若您已經得到滿意的答覆,請適時結案!! --
-- 欲知前世因,今生受者是;欲知來世果,今生做者是 --
-- 一切有為法,如夢幻泡影,如露亦如電,應作如是觀 --
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#5 引用回覆 回覆 發表時間:2002-12-13 10:16:22 IP:61.218.xxx.xxx 未訂閱
引言: 各位大大...小弟有一個DirectPlay的問題 小弟我最近的專題是要寫一個小遊戲ㄚ 要用DirectPlay作連線的功能 但是有關這方面的範例和說明很少,而且Microsoft SDK 又很複雜看不太懂....請問各位大大可以指點一下 如有簡單的範例程式是最好的.....謝謝啦..^^
給您參考一下: A DirectPlay Tutorial V 1.2 11-21-2001 by Sobeit Void Updates V1.2 (21-11-2001) - Fixed typo in Create_TCP_Connection code snippet - CoUnIntialize() should be CoUninitialize() - IID_LPDIRECTPLAYLOBBY2A should be IID_IDirectPlayLobby2A V1.1 (11-4-2000) Abstract Seeing that there are few (if any) tutorials on DirectPlay, and that it is such a pain to learn DirectPlay from the MSDN (like I have), I thought I would alleviate some of the agony by writing this tutorial. Also I have this day off since I'm waiting for the monstrous 100 MB DX 7 SDK download. If you find any errors, please email me at robin@cyberversion.com What This Tutorial is About The tutorial will use Directx 5.0 or the IDirectPlay3 interface. I know DX 7.0 is out but I am still downloading it and I passed with DC 6. Although DC 5.0 is inefficient (every packet has high overhead), it seems the new versions are getting better. Anyway, the idea should mainly be the same. The demo will be a chat program that I am using for my game. It is not a lobby server (a lobby server is something like Blizzard's BattleNet), just a client/server program. The user is assumed to be familiar with C/C and some Win32 programming (Of course, DX too). Some networking concepts would help. No MFC, I hate MFC. Putting another layer on Win32 API bothers me when you can access Win32 functions directly. Also, I will only use TCP/IP. If you want to use IPX, modem, or cable, check out the MSDN. They are roughly equivalent. This tutorial by no means teaches you everything about DirectPlay. Any networking application is a pain to write, and you should consult the MDSN documentation for more information (mostly where I learnt DirectPlay from). There is no sample demo exe too as I don't have time to write one out to show you. Let's get started What DirecPlay is About DirectPlay is a layer above your normal network protocols (IPX, TCP/IP etc). Once the connection is made, you can send messages without needing to know what the user is connecting with. This is one of the best features (IMO) and though some of you may come up with more efficient messaging protocols with WinSock, I'd rather use DirectPlay and save me hordes of trouble. BTW, DirectPlay uses Winsock too. Sessions in DirectPlay A DirectPlay session is a communication channel between several computers. An application must be in a session before it can communicate with other machines. An application can either join an existing session or create a new session and wait for people to join it. Each session has one and only one host, which is the application that creates it. Only the host can change the session properties (we will get to that detail later). The default mode in a session is peer-to-peer, meaning that the session complete state is replicated on all the machines. When one computer changes something, other computers are notified. The other mode is client/server, which means everything is routed through a server. You manage the data in this mode, which is probably the best in a chat program. However, I used sort of a hybrid in this tutorial. Players in DirectPlay An application must create a player to send and receive message. Messages are always directed to a player and not the computer. At this point, your application should not even know about the computer's location. Every message you sent is directed at a specific player and every received message is directed at a specific local player (except for system messages; more on that later). Players are identified only as local (exist on your computer) or remote (exist on another computer). Though you can create more than one local player on your machine, I find it not very useful. DirectPlay offers additional methods to store application specific data so you don't have to implement a list of players but I'd rather do my own list. You can also group players in the same session together so any message sent to you will get directed to the group. This is great for games where you can ally with other people and you want to send messages to your allies only. Unfortunately, you have to explore that area on your own. If at this point you find all this very troublesome, try writing your own network API. You will be glad what DirectPlay does for you. Messages in DirectPlay Now we have a session and a player, we can start sending messages. As I said, each message sent is marked by a specific local player and can be sent to any player. Each message received is placed in a queue for that local player. Since I create only one player, all the messages belong to that player. You don't need to bother about this queue; all you need is to extract the messages from it and act on them. The application can poll the receive queue for messages or use a separate thread and events. I tried the thread method and it works great, except for when I use MessageBox to notify the user (in case you don't know, MessageBox displays a message box in Windows). If you want to use threads, you need to pause the thread when the application displays a message box and somehow it gets very messy with synchronization. So I opted for the poll method. Feel free to use threads if you think you can handle it. There are two types of messages: player and system. Player messages have a sender and receiver. System messages are sent to every player and are marked sent from the system (DPID_SYSMSG). DP stands for DirectPlay, ID for identification. If you cannot understand messages, go and learn more about DirectDraw first. System messages are generated when the session state is changed, i.e. when a new player joins the session. Note: There are also security features using the Security Support Provider Interface (SSPI) on windows. These messages are encrypted and such. I don't think this is of much use in gaming. Actual implementation Whew. Now we get to more details. If you didn't quite understand any of the above, please read them again till you do. If you have any questions like "What if..." it will be answered soon. So let's move on. The first thing to do is to include the DirectPlay header files:
#include      // directplay main
#include    // the directplay lobby 
Also add DPLAYX.LIB to your project. If you are wondering why there is a dplay.lib and dplayx.lib, add the dplayx.lib cause I think there are more methods there used in the lobby. Also if you are asking why am I including the dplobby methods when I am not using a lobby server, it will become clearer later. Also you need to define INITGUID or add the dxguid.lib. Define this at the very top of your project. #define INITGUID // to use the predefined ids Next you need to give you application a GUID (Global Unique Id). This ID is to distinguish the application in the computer. You don't want your application to send messages to your browser, only your application. You can use guidgen.exe to create a id for your application. Microsoft guarantees that it will never mathematically create the same GUID twice, so we take their word for it. It will look something like
DEFINE_GUID(our_program_id,
0x5bfdb060, 0x6a4, 0x11d0, 0x9c, 0x4f, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e);
Now to define our globals
LPDIRECTPLAY3A       lpdp = NULL;        // interface pointer to directplay
LPDIRECTPLAYLOBBY2A  lpdplobby = NULL;   // lobby interface pointer
If you are wondering what the A behind the interface stands for, it means ANSI version. There are two versions for DirectPlay ?ANSI and Unicode. (Unicode is a standard for using 16 bits to represent a character instead of 8 bits, just for internationalization. Just use the ANSI version and forget about supporting multiple languages. Makes everybody happy.) The next thing is the main loop of the program. This is the bare skeleton of what it looks like. // Get information from local player // Initialize Directplay connection from the information from above
while(1)
{
    if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)
    {
        // Your normal translating message        } // end if PeekMessage(..)
    else
    {
        // Your game loop            // Receive  messages ?We will implement this
    } // end else
} // end of while(1)    // Close Directplay Connection
How you get information from the user is up to you. You can do it via a dialog box or any other way you deem. The main thing you need to get is the name and whether if it is the server or client. If it is the server, you get the session name. If it is the client, you get the TCP/IP address to connect to. Anyway, we store those in a global below:
BOOL   gameServer;         // flag for client/server
char   player_name[10];    // local player name, limit it to 10 chars
char   session_name[10];   // name of session, also limit to 10 chars
char   tcp_address[15];    // tcp ip address to connect to
I'm sorry if you are firmly against globals. Feel free encapsulate them, but I think globals simplify the learning process here. Also I do not do much error checking here; theoretically you should test the result of every function call. Now before we move on, we should implement a list of players in the current session. Although it will not be necessary here, you will need it in larger applications. You create a list with the following item element:
Class DP_PLAYER_LIST_ELEM{    DPID    dpid;      // the directplay id of player
char    name[10];  // name of player
DWORD   flags;     // directplay player flags    // any other info you might need
};
You need to implement a list class that adds a player and deletes a player with a specific dpid. Do not reference the players by their names because players can have the same name. Use the id to differentiate players. Due to space constraints, I will not include any code here. Alternatively you can use arrays to hold the global player information, but this is not scalable and more troublesome. Then we define a global pointer to the class: DP_PLAYER_LIST *dp_player_list; // list of players in current session That is about all the globals you need. You should also create a local player struct for additional information for local players only. Setting up Connection Initialization To set up the connection, we will write a function that takes a TCP/IP string and create a TCP/IP connection. This is also where the lobby interface comes in. Side note: Although DirectPlay has a method for enumerating the connections available, the method will enumerate all connections even if they are not available. So if you do not have an IPX connection, the enumeration will return an IPX option to connect and the connection would fail only when the user tries to make the connection. This seems redundant to me so I recommend you skip this part and give the options to the user straight, failing only when the user tries to make the connection. In case you don't know about enumeration, we will talk more about it later. Enumerating things in DirectX is for the user to provide a function that is called (repeatedly) by a process in DirectX. This is known as a callback function. Here goes the function. Remember you should do error checking for every function call.
int Create_TCP_Connection(char *IP_address)
{
    LPDIRECTPLAYLOBBYA        old_lpdplobbyA = NULL;    // old lobby pointer
    DPCOMPOUNDADDRESSELEMENT  Address[2];               // to create compound addr
    DWORD     AddressSize = 0;       // size of compound address
    LPVOID    lpConnection= NULL;    // pointer to make connection        CoInitialize(NULL);    // registering COM        // creating directplay object
    if  ( CoCreateInstance(CLSID_DirectPlay, NULL, CLSCTX_INPROC_SERVER,
         IID_IDirectPlay3A,(LPVOID*)&lpdp ) != S_OK)
    {
        // return  a messagebox error
        CoUninitialize();   // unregister the comp
        return(0);
    }        // creating lobby object
    DirectPlayLobbyCreate(NULL, &old_lpdplobbyA, NULL, NULL, 0);        // get new interface of lobby
    old_lpdplobbyA->QueryInterface(IID_IDirectPlayLobby2A, (LPVOID *)&lpdplobby));        old_lpdplobbyA->Release();   // release old interface since we have new one        // fill in data for address
    Address[0].guidDataType = DPAID_ServiceProvider;
    Address[0].dwDataSize   = sizeof(GUID);
    Address[0].lpData       = (LPVOID)&DPSPGUID_TCPIP;  // TCP ID        Address[1].guidDataType = DPAID_INet;
    Address[1].dwDataSize   = lstrlen(IP_address) 1;
    Address[1].lpData       = IP_address;        // get size to create address
    // this method will return DPERR_BUFFERTOOSMALL ?not an error
    lpdplobby->CreateCompoundAddress(Address, 2, NULL, &Address_Size);        lpConnection = GlobalAllocPtr(GHND, AddressSize);  // allocating mem        // now creating the address
    lpdplobby->CreateCompoundAddress(Address, 2, lpConnection, &Address_Size);        // initialize the tcp connection
    lpdp->InitializeConnection(lpConnection, 0);        GlobalFreePtr(lpConnection);        // free allocated memory        return(1);        // success    }        // end int Create_TCP_Connection(..)
First we initialize COM to increment its count by 1 and we create the DirectPlay object using CoCreateInstance. This is another method of creating DirectX objects, which is actually the method the wrapper function uses. We have to pass in the class identifier and such but the main thing to note is the IID_DirectPlay3A parameter. This is the identifier of the IDirectPlay3A interface. So if you want to get an IDirectPlay2A interface, set the parameter to IID_DirectPlay2A. Similarly if you want an IDirectPlay4A interface. Then we create the DirectPlay lobby object and we query for the 2A version. Since there is a macro DirectPlayLobbyCreate, we do not need to initalize COM like above. Underneath, this function does the same (COM and CoCreateInstance) except it gets the lowest interface, ie IDirectPlayLobbyA. So we need to get the 2A version, which is done by querying it with the interface identifier (note you query all DirectX objects in the same manner). Then we close the old lobby since we have a new one. Next we create an address that holds the information about the TCP connection. Since we did not use EnumConnections, we need to build that information ourselves. You can use the information from the enumeration and jump straight to initialize but I prefer to reduce callback functions to a minimum. We set the fields of the address structure and we set the type to the TCP/IP service provider id. This is from defined in dxguid.lib or the including INITGUID above. We set the second element to the address string. You can get all these information from the MSDN, and which parameters to pass to create other types of connection. Then we get the size of buffer required for the connection by passing in a NULL parameter. The size required will be stored in the variable Address_Size. Note this method will return DPERR_BUFFERTOOSMALL since we are getting the size. Do not interpret this as an error condition. We then allocate memory from the system heap using GlobalAllocPtr, rather than using malloc. We then create the address information by passing the allocated buffer to the function again. Using that we call the DirectPlay Initialize method and we would have created a TCP/IP connection. If InitalizeConnection returns DPERR_UNAVAILABLE, it means the computer cannot make such a connection and it's time to inform the user no such protocol exists on the computer. People may wonder why I chose to do things the hard way when I could have used DirectPlayCreate and be happy. Well, the main thing is I want to override the default dialog boxes that would pop up to ask the user to enter the information. You don't see that in StarCraft, do you? (Sorry, love Blizzard) Do note that you should test every function call and return the appropriate error. I do not do that here because of space and also I'm lazy. Also as for how this function works, you pass "" as the string if you are the hosting the session, else you pass the IP of the machine you are connecting to. So in the "Getting user information", you should have enough information to call this function like:
if (gameServer)                 // if host machine
    Create_TCP_Connection("");  // empty string is enough
else
    Create_TCP_Connection(tcp_address);  // passed the global ip from user
If you think this is the end of connecting, think again. Remember, we need to have a session and a player before we send messages. But before that let's close the connection.
int        DirectPlay_Shutdown()
{
    if (lpdp)   // if connection already up, so it won't be null
    {
        if (lpdplobby)
            lpdplobby->Release();            lpdp->Release();            CoUninitialize();  // unregister the COM
    }        lpdp       = NULL;  // set to NULL, safe practice here
    lpdplobby  = NULL;        return(1);  // always success
}  // end int DirectPlay_Shutdown();
Stick this function at the close connection section. Sessions You will have no choice but to do a callback function here. The main functions you mainly need to do session management are: EnumSessions - enumerates all the session available sessions Open - joins or hosts a new session Close - close the session GetSessionDesc - get session properties SetSessionDesc - set session properties I will only talk about the 3 functions above that we will use in this tutorial. Once you understand them, it is very easy to understand the others. lpdp->Close(); Simple. Just close the session before you call DirectPlay_Shutdown(): All the local players created will be destroyed and the DPMSG_DESTROYPLAYERORGROUP will be sent to other players in the session. lpdp->EnumSessions(..); // enumerates all the sessions This function will only be called by the client side so if you are hosting the session, call Open. What is so troublesome is that the client needs to search for a session to join when they have made a connection, and since there may be more than one session in the host, we need to get every available session and present them to the user. This will require the use of a callback function. The callback function prototype is:
BOOL FAR PASCAL EnumSessionsCallback2(LPCDPSESSIONDESC2 lpThisCD,
                                      LPDWORD  lpdwTimeOut,
                                      DWORD    dwFlags,
                                      LPVOID   lpContext);
Things to note: This callback function that we implement will be called once for each session that is found using EnumSessions. Once all the sessions are enumerated, the function will be called one more time with the DPESC_TIMEOUT flag. Any pointers returned in a callback function are only temporary and are only valid in the callback function. We must save any information we want from the enumeration. This applies to the player enumeration later. EnumSessions has the prototype:
EnumSessions(LPDPSESSIONDESC2 lpsd, DWORD dwTimeOut,
             LPDPENUMSESSIONSCALLBACK2 lpEnumSessionCallback2,
             LPVOID context, DWORD dwFlags);
If you wondering why there is a 2 behind certain typedefs, the two means the second version of the type. A rule of thumb is to always use the latest typedefs as they encapsulate more things. The first parameter is a session descriptor so you need to initialize one.
DPSESSIONDESC2   session_desc;    // session desc    ZeroMemory(&session_desc, sizeof(DPSESSIONDESC2));  // clear the desc
session_desc.dwSize = sizeof(DPSESSIONDESC2);
session_desc.guidApplication = our_program_id;      // we define this earlier
This will ensure our program only returns sessions hosted by our program. ZeroMemory is similar to memset to 0 and as you should know, many DirectC calls require you to put the size in the structure passed. The second parameter should be set to 0 (recommended) for a default timeout value. The third parameter is the callback function so we pass the function to it The fourth parameter is a user-defined context that is passed to the enumeration callback. I will describe how to use it later. The fifth is the type of sessions to enumerate. Just pass it the default 0 meaning it will only enumerate available sessions. Check out the MSDN for more options. All these seem to be a good candidate for a function so let's do it.
// our callback function
BOOL FAR PASCAL EnumSessionsCallback(LPCDPSESSIONDESC2 lpThisSD,
                                     LPDWORD lpdwTimeOut,
                                     DWORD dwFlags, LPVOID lpContext)
{
    HWND   hwnd;      // handle. I suggest as listbox handle        if (dwFlags & DPESC_TIMEOUT)  // if finished enumerating stop
        return(FALSE);        hwnd = (HWND) lpContext;   // get window handle        // lpThisSd-> lpszSessionNameA  // store this value, name of session
    // lpThis->guidInstance         // store this, the instance of the host        return(TRUE);  // keep enumerating
} // end callback    // our enumeration function
int EnumSessions(HWND hwnd, GUID app_guid, DWORD dwFlags)
{
    DPSESSIONDESC2   session_desc;   // session desc        ZeroMemory(..);    // as above
    // set size of desc
    // set guid to the passed guid        // enumerate the session. Check for error here. Vital
    lpdp->EnumSessions(&session_desc, 0, EnumSessionsCallback, hwnd, dwFlags);        return(1);   // success
}        // end int EnumSessions
I suggest sending a listbox handle as the context so we can save the information and display it to the user. In the callback function, you must allocate space to hold the guidInstance. This is the instance of the program that is hosting the session. You need to pass this information for the user to select which one session. I have commented out the name and the instance. You save them in whatever way you want. Declare a global array and fill in the member. Whatever. I suggest a listbox so you can send messages via the hwnd parameter. Remember, the instance and name are only valid inside the callback function so you must save them to be able to present them later. Note: If you return false in the callback, the enumeration will stop and return control the EnumSessions. If you don't stop it, it will loop forever. Also return false if you encounter an error inside. It is imperative you check the value from EnumSessions especially if you are not enumerating available sessions only. Also, the whole program will block while you are enumerating because it has to search for sessions. You can do an asynchronous enumeration too. Check it out yourself. lpdp->Open(LPDPSESSIONDESC2 lpsd, DWORD dwFlags) This functions hosts or joins a session using the dwFlags. If you are joining a session, you only need to fill the dwSize and guidInstance (you saved it somewhere) of the descriptor. So we define a descriptor as:
DPSESSIONDESC2   session_desc;    ZeroMemory(&session_desc, sizeof(DPSESSIONDESC2));
session_desc.dwSize = sizeof(DPSESSIONDESC2);
session_desc.guidInstance = // instance you have save somewhere;    // and join the session
lpdp->Open(&session_desc, DPOPEN_JOIN);
If you are hosting a session, you need to fill in, in addition to the above, the name of the session, the maximum number of players allowed in the session and the session flags. You set the flag to Open as DPOPEN_CREATE | DPOPEN_RETURNSTATUS. The return status flag hides a dialog box displaying the progress status and returns immediately. Although the documentation says I should keep calling Open with the return status flag, I have not found a need to do so (nor can I comprehend the reason they gave). You can change the session properties if you are a host using the other two methods I didn't cover. The flags in the session desc you should set in this tutorial are: DPSESSION_KEEPALIVE ?keeps the session alive when players are abnormally dropped. DPSESSION_MIGRATEHOST ?if the current host exits, another computer will become the host. You can Or the flags together like so: FLAG_1 | FLAG_2 | FLAG_3. Check out the MSDN for more flag options. Player Creation We are just about done setting up. Now we create a local player using CreatePlayer. You have to define a name struct like so: code] DPNAME name; // name type DPID dpid; // the dpid of the player created given by directplay ZeroMemory(&name,sizeof(DPNAME)); // clear out structure name.size = sizeof(DPNAME); name.lpszShortNameA = player_name; // the name the from the user name.lpszLongNameA = NULL; lpdp->CreatePlayer(&dpid, &name, NULL, NULL, 0, player_flags);
This function will return a unique id for the local player within the session. Use this to identify the player rather than using the name. Save this in the local player struct. The player_name passed is obtained earlier in the information asked from user. The middle parameters are used if you do not want to poll the receive queue. Use them if you want to do multithreading. The player flags is either DPPLAYER_SERVERPLAYER or 0, which means non-server player. There can only be one server player in a session. There is also a spectator player but its meaning is defined by the application so we don't use it here. The other function needed is EnumPlayers. I know you all are masters at enumerating now so I leave you all to implement this. Remember the global list of players we defined earlier? Just add the player inside the callback. It works the same way as the enumeration above. You don't have to enumerate the players in this chat but it is cool that you can see who is also connected at the same time. You do not need to destroy the player because closing the session does that automatically and I don't see why you need to destroy and create another player while you are still connected. Still, it is your application. Message Management Now that we have a player, we need to know how to send and receive messages. I will talk about sending messages first. Sending Messages There is only one way to send a message and that is through the Send function. (Actually there is another if you use a lobby). If you remember, sending a message requires the id of the sender and the receiver. Good thing you have saved the local player id in a local player struct and all the players' ids in a global player list. So call the function as follows:
lpdp->Send(idFrom, idTo, dwFlags, lpData, dwDataSize);
The idFrom is the id of the local player. This must be set to a locally created player only (we don't want to impersonate another player, do we?) which in most cases is only one. The idTo is the receiver's id. Use DPID_SERVERPLAYER to send to the server only and DPID_ALLPLAYERS to send to everybody (except yourself). Note you cannot send a message to yourself. If you want to direct the message to a specific player, use the player list. You do not have to use a player list in a chat; instead you can add everything to a listbox. But later in the game, a list of players comes handy. The flag parameter is how should the message be sent. The default is 0, which means non-guaranteed. The other options are DPSEND_GUARANTTED, DPSEND_ENCRYPTED and DPSEND_SIGNED. Sending a guaranteed message can take more than 3 times longer than a non-guaranteed one so only use guaranteed sending for important messages (like text). The signed and encrypted messages require a secure server, which we did not setup. Also any message received is guaranteed to be free of corruption (DirectPlay performs integrity checks on them). Something to note: If you create a session that specifies no message id, their message idFrom will make no sense and the receiver will receive a message from DPID_UNKNOWN. Why anyone would want to disable message id is beyond me. The last two parameters are a pointer to the data to send and the size of that block. Note that DirectPlay has no upper limit of the size you can send. DirectPlay will break large messages into smaller packets and reassemble them at the other end. Beware when sending non-guaranteed messages too; if one packet is lost, the whole message is discarded. Since we are doing a chat program, I will show an example of sending a chat message
// types of messages the application will receive
const        DWORD        DP_MSG_CHATSTRING = 0;  // chat message    // the structure of a string message to send
typedef struct DP_STRING_MSG_TYP   // for variable string
{
    DWORD  dwType;     // type of message
    char   szMsg[1];   // variable length message    } DP_STRING_MSG. *DP_STRING_MSG_PTR;    // function to send string message from local player
int DP_Send_String_Mesg(DWORD type, DPID idTo, LPSTR lpstr)
{
    DP_STRING_MSG_PTR   lpStringMsg;    // message pointer
    DWORD               dwMessageSize;  // size of message
        
    // if empty string, return        dwMessageSize = sizeof(DP_STRING_MSG) lstrlen(lpstr); // get size        // allocate space
    lpStringMsg = (DP_STRING_MSG_PTR)GlobalAllocPtr(GHND, dwMessageSize);        lpStringMsg->dwType = type;          // set the type
    lstrcpy(lpStringMsg->szMsg, lpstr);  // copy the string        // send the string
    lpdp->Send(local_player_id,idTo, DP_SEND_GUARANTEED,
        lpStringMsg, dwMessageSize);
        
    GlobalFreePtr(lpStringMsg);    // free the mem        return(1);   // success
} // end int DP_Send_String_Mesg(..)
We first define the types of messages we can have. Since this is a chat program, there can only be one type, which I set to DP_MSG_CHATSTRING. You may add others and set the type so you can reuse the string sending function for different things. That is why the string message struct has a type to differentiate the string contents. The send function basically allocates space and sends the function to the desired player. Note the local_player_id is stored somewhere globally, or you can set it to pass another variable to set the local_player_flag. Do check the errors returned especially with allocation routines. Receiving Messages Receiving messages requires slightly more work than sending. There are two types of messages we can receive ?a player message and a system message. A system message is sent when a change in the session state occurs. The system messages we trapped in this chat are:

DPSYS_SESSIONLOST          - the session was lost
DPSYS_HOST                 - the current host has left and you are the new host
DPSYS_CREATEPLAYERORGROUP  ?a new player has join
DPSYS_DESTROYPLAYERORGROUP ?a player has left

        
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#6 引用回覆 回覆 發表時間:2002-12-13 10:25:16 IP:61.218.xxx.xxx 未訂閱
引言: 各位大大...小弟有一個DirectPlay的問題 小弟我最近的專題是要寫一個小遊戲ㄚ 要用DirectPlay作連線的功能 但是有關這方面的範例和說明很少,而且Microsoft SDK 又很複雜看不太懂....請問各位大大可以指點一下 如有簡單的範例程式是最好的.....謝謝啦..^^
這有個元件是可以用看看 http://bcb-tools.com/Welcome.htm The TDx_Play_Library is a library of 14 Borland C Builder (BCB) rapid application development (RAD) components that "wrap" all available DirectPlay(tm) functionality into easy-to-use BCB Visual Component Library (VCL) components, supplying a complete framework for harnessing all the power of DirectPlay(tm) and Borland C Builder. These components make DirectPlay(tm) very easy to use, make DirectPlay(tm) work with Borland C Builder, have many added extras designed to help you mix DirectPlay(tm) and Borland C Builder - quickly and easily. All components come complete with full source code, project files, auto-installer, full [F1] context-sensitive help covering all of DirectPlay(tm) plus additional features, and demos with full source code. -------------------------------------------------------------------------------- These components offer advanced networking support, including :- Multi-Player Internet Local Area Network Dial-Up Connection Direct Cable Connection -------------------------------------------------------------------------------- By fully utilizing the underlying DirectPlay(tm) engine, the TDx_Play_Library supplies :- Realtime network message handling using BCB-style event notifications A new programming paradigm, rapid application development DirectPlay, or "RAD DirectX" for short. -------------------------------------------------------------------------------- The TDx_Play_Library contains much added functionality designed to make your life significantly easier, including :- Fully optimized for BCB and the VCL A solution to integration problems between BCB and DirectX where necessary. Automatic error detection & redirection to easy-to-use red>網聯盟----Visita網站http://www.vista.org.tw ---[ 發問前請先找找舊文章 ]---
系統時間:2017-10-22 9:11:13
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!