You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
444 lines
15 KiB
C#
444 lines
15 KiB
C#
//----------------------------------------------------------------------------
|
|
// File: ChatPeer.cs
|
|
//
|
|
// Desc: The main game file for the ChatPeer sample. It connects
|
|
// players together with dialog boxes to prompt users on the
|
|
// connection settings to join or create a session. After the user
|
|
// connects to a session, the sample displays the chat dialog.
|
|
//
|
|
// After a new game has started the sample begins a very simplistic
|
|
// chat session where users can send text to each other.
|
|
//
|
|
// This sample Interops with the C++ and VB.NET version of the
|
|
// sample as well.
|
|
//
|
|
// Copyright (c) Microsoft Corp. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.Windows.Forms;
|
|
using Microsoft.DirectX;
|
|
using Microsoft.DirectX.DirectPlay;
|
|
|
|
|
|
namespace ChatPeerSample
|
|
{
|
|
/// <summary>
|
|
/// Summary description for ChatPeer.
|
|
/// </summary>
|
|
public class ChatPeer : System.Windows.Forms.Form
|
|
{
|
|
private System.Windows.Forms.Button btnExit;
|
|
private System.Windows.Forms.Label label1;
|
|
private System.Windows.Forms.GroupBox groupBox1;
|
|
private System.Windows.Forms.Label lblUsers;
|
|
private System.Windows.Forms.Button btnSend;
|
|
private System.Windows.Forms.TextBox txtChat;
|
|
private System.Windows.Forms.TextBox txtSend;
|
|
private delegate void PeerCloseCallback(); // This delegate will be called when the session terminated event is fired.
|
|
|
|
|
|
/// <summary>
|
|
/// Our players structure
|
|
/// </summary>
|
|
public struct Players
|
|
{
|
|
public int playerId;
|
|
public string Name;
|
|
public Players(int id, string n)
|
|
{ playerId = id; Name = n; }
|
|
}
|
|
|
|
private const int MaxChatStringLength = 508;
|
|
private const byte ChatMessageId = 1;
|
|
private const int DefaultPort = 2502;
|
|
|
|
// Local variables for this app
|
|
public Peer peerObject = null; // Main DPlay object
|
|
private ConnectWizard connectWizard = null; // The wizard to create/join a DPlay Session
|
|
private ArrayList playerList = new ArrayList();
|
|
private int localPlayerId;
|
|
|
|
// This GUID allows DirectPlay to find other instances of the same game on
|
|
// the network. So it must be unique for every game, and the same for
|
|
// every instance of that game. // {876A3036-FFD7-46bc-9209-B42F617B9BE7}
|
|
// However, we are using the same guid the C++ and VB.NET version of the
|
|
// samples use so they can all communicate together.
|
|
public Guid localApplicationGuid = new Guid(0x876a3036, 0xffd7, 0x46bc, 0x92, 0x9, 0xb4, 0x2f, 0x61, 0x7b, 0x9b, 0xe7);
|
|
|
|
|
|
/// <summary>
|
|
/// Constuctor
|
|
/// </summary>
|
|
public ChatPeer()
|
|
{
|
|
try
|
|
{
|
|
// Load the icon from our resources
|
|
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(this.GetType());
|
|
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
|
}
|
|
catch
|
|
{
|
|
// It's no big deal if we can't load our icons, but try to load the embedded one
|
|
try { this.Icon = new System.Drawing.Icon(this.GetType(), "directx.ico"); }
|
|
catch {}
|
|
}
|
|
|
|
//
|
|
// Required for Windows Form Designer support
|
|
//
|
|
InitializeComponent();
|
|
|
|
peerObject = new Peer();
|
|
// First set up our event handlers (We only need events for the ones we care about)
|
|
peerObject.PlayerCreated += new PlayerCreatedEventHandler(this.PlayerCreated);
|
|
peerObject.PlayerDestroyed += new PlayerDestroyedEventHandler(this.PlayerDestroyed);
|
|
peerObject.HostMigrated += new HostMigratedEventHandler(this.HostMigrated);
|
|
peerObject.Receive += new ReceiveEventHandler(this.DataReceived);
|
|
peerObject.SessionTerminated += new SessionTerminatedEventHandler(this.SessionTerminated);
|
|
connectWizard = new ConnectWizard(peerObject, localApplicationGuid, "Chat Peer");
|
|
|
|
connectWizard.DefaultPort = DefaultPort;
|
|
if (connectWizard.StartWizard())
|
|
{
|
|
// Great we've connected (or joined).. Now we can start the sample
|
|
// Are we the host?
|
|
if (connectWizard.IsHost)
|
|
this.Text += " (HOST)";
|
|
}
|
|
else
|
|
// We obviously didn't want to start a session
|
|
this.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clean up any resources being used.
|
|
/// </summary>
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
this.Hide();
|
|
base.Dispose(disposing);
|
|
|
|
// Cleanup DPlay
|
|
if (peerObject != null)
|
|
peerObject.Dispose();
|
|
|
|
peerObject = null;
|
|
}
|
|
|
|
#region Windows Form Designer generated code
|
|
/// <summary>
|
|
/// Required method for Designer support - do not modify
|
|
/// the contents of this method with the code editor.
|
|
/// </summary>
|
|
private void InitializeComponent() {
|
|
this.txtSend = new System.Windows.Forms.TextBox();
|
|
this.txtChat = new System.Windows.Forms.TextBox();
|
|
this.btnSend = new System.Windows.Forms.Button();
|
|
this.lblUsers = new System.Windows.Forms.Label();
|
|
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
|
this.label1 = new System.Windows.Forms.Label();
|
|
this.btnExit = new System.Windows.Forms.Button();
|
|
this.groupBox1.SuspendLayout();
|
|
this.SuspendLayout();
|
|
//
|
|
// txtSend
|
|
//
|
|
this.txtSend.Location = new System.Drawing.Point(14, 205);
|
|
this.txtSend.MaxLength = 508;
|
|
this.txtSend.Name = "txtSend";
|
|
this.txtSend.Size = new System.Drawing.Size(280, 21);
|
|
this.txtSend.TabIndex = 5;
|
|
this.txtSend.Text = "";
|
|
this.txtSend.TextChanged += new System.EventHandler(this.SendTextChanged);
|
|
//
|
|
// txtChat
|
|
//
|
|
this.txtChat.Location = new System.Drawing.Point(15, 45);
|
|
this.txtChat.Multiline = true;
|
|
this.txtChat.Name = "txtChat";
|
|
this.txtChat.ReadOnly = true;
|
|
this.txtChat.Size = new System.Drawing.Size(354, 152);
|
|
this.txtChat.TabIndex = 3;
|
|
this.txtChat.Text = "";
|
|
//
|
|
// btnSend
|
|
//
|
|
this.btnSend.Enabled = false;
|
|
this.btnSend.Location = new System.Drawing.Point(297, 204);
|
|
this.btnSend.Name = "btnSend";
|
|
this.btnSend.Size = new System.Drawing.Size(72, 22);
|
|
this.btnSend.TabIndex = 4;
|
|
this.btnSend.Text = "&Send";
|
|
this.btnSend.Click += new System.EventHandler(this.btnSend_Click);
|
|
//
|
|
// lblUsers
|
|
//
|
|
this.lblUsers.Location = new System.Drawing.Point(198, 18);
|
|
this.lblUsers.Name = "lblUsers";
|
|
this.lblUsers.Size = new System.Drawing.Size(37, 14);
|
|
this.lblUsers.TabIndex = 1;
|
|
this.lblUsers.Text = "0";
|
|
this.lblUsers.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
|
//
|
|
// groupBox1
|
|
//
|
|
this.groupBox1.Controls.Add(this.txtSend);
|
|
this.groupBox1.Controls.Add(this.btnSend);
|
|
this.groupBox1.Controls.Add(this.txtChat);
|
|
this.groupBox1.Controls.Add(this.btnExit);
|
|
this.groupBox1.Controls.Add(this.lblUsers);
|
|
this.groupBox1.Controls.Add(this.label1);
|
|
this.groupBox1.Location = new System.Drawing.Point(9, 6);
|
|
this.groupBox1.Name = "groupBox1";
|
|
this.groupBox1.Size = new System.Drawing.Size(377, 235);
|
|
this.groupBox1.TabIndex = 0;
|
|
this.groupBox1.TabStop = false;
|
|
//
|
|
// label1
|
|
//
|
|
this.label1.Location = new System.Drawing.Point(11, 18);
|
|
this.label1.Name = "label1";
|
|
this.label1.Size = new System.Drawing.Size(183, 14);
|
|
this.label1.TabIndex = 0;
|
|
this.label1.Text = "Number of people in conversation: ";
|
|
//
|
|
// btnExit
|
|
//
|
|
this.btnExit.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
|
this.btnExit.Location = new System.Drawing.Point(294, 16);
|
|
this.btnExit.Name = "btnExit";
|
|
this.btnExit.Size = new System.Drawing.Size(72, 22);
|
|
this.btnExit.TabIndex = 2;
|
|
this.btnExit.Text = "E&xit";
|
|
this.btnExit.Click += new System.EventHandler(this.btnExit_Click);
|
|
//
|
|
// ChatPeer
|
|
//
|
|
this.AcceptButton = this.btnSend;
|
|
this.AutoScaleBaseSize = new System.Drawing.Size(5, 14);
|
|
this.CancelButton = this.btnExit;
|
|
this.ClientSize = new System.Drawing.Size(394, 253);
|
|
this.Controls.Add(this.groupBox1);
|
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
|
this.MaximizeBox = false;
|
|
this.MinimizeBox = false;
|
|
this.Name = "ChatPeer";
|
|
this.Text = "C# Chat Peer Sample";
|
|
this.groupBox1.ResumeLayout(false);
|
|
this.ResumeLayout(false);
|
|
}
|
|
#endregion
|
|
#region DirectPlayEvents
|
|
|
|
|
|
/// <summary>
|
|
/// A player was created
|
|
/// </summary>
|
|
private void PlayerCreated(object sender, PlayerCreatedEventArgs e)
|
|
{
|
|
// Get the PlayerInformation and store it
|
|
PlayerInformation peerInfo = peerObject.GetPeerInformation(e.Message.PlayerID);
|
|
Players oPlayer = new Players(e.Message.PlayerID,peerInfo.Name);
|
|
// We lock the data here since it is shared across multiple threads.
|
|
lock (playerList)
|
|
{
|
|
playerList.Add(oPlayer);
|
|
// Update our number of players and our button
|
|
lblUsers.Text = playerList.Count.ToString();
|
|
}
|
|
// Save this player id if it's ourselves
|
|
if (peerInfo.Local)
|
|
localPlayerId = e.Message.PlayerID;
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// A player was destroyed
|
|
/// </summary>
|
|
private void PlayerDestroyed(object sender, PlayerDestroyedEventArgs e)
|
|
{
|
|
// Remove this player from our list
|
|
// We lock the data here since it is shared across multiple threads.
|
|
lock (playerList)
|
|
{
|
|
foreach (Players player in playerList)
|
|
{
|
|
if (e.Message.PlayerID == player.playerId)
|
|
{
|
|
playerList.Remove(player);
|
|
break;
|
|
}
|
|
}
|
|
// Update our number of players and our button
|
|
lblUsers.Text = playerList.Count.ToString();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// The host was migrated, see if you're the new host
|
|
/// </summary>
|
|
private void HostMigrated(object sender, HostMigratedEventArgs e)
|
|
{
|
|
if (localPlayerId == e.Message.NewHostID)
|
|
{
|
|
// I'm the new host, update my UI
|
|
this.Text += " (HOST)";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// We've received data, parse it
|
|
/// </summary>
|
|
private void DataReceived(object sender, ReceiveEventArgs e)
|
|
{
|
|
if ((byte)e.Message.ReceiveData.Read(typeof(byte)) == ChatMessageId) // We've received text chat
|
|
{
|
|
// We won't be using the helper functions here since we want to
|
|
// interop with the c++ version of the app. It packages it's messages
|
|
// up with the first byte being the msg id (ChatMessageId), and
|
|
// the next xxx bytes as an ANSI string.
|
|
|
|
// Get the default ASCII decoder
|
|
System.Text.Decoder dec = System.Text.Encoding.ASCII.GetDecoder();
|
|
int length = (int)e.Message.ReceiveData.Length - 1;
|
|
// Create a char array of the right length
|
|
byte[] data = (byte[])e.Message.ReceiveData.Read(typeof(byte), length);
|
|
char[] c = new char[dec.GetCharCount(data, 0, length)];
|
|
// Get the actual decoded characters
|
|
dec.GetChars(data, 0, length, c, 0);
|
|
// Now we can use the string builder to actually build our string
|
|
System.Text.StringBuilder sb = new System.Text.StringBuilder(c.Length);
|
|
sb.Insert(0, c, 0, dec.GetCharCount(data, 0, length));
|
|
|
|
string sChatText = sb.ToString(); // The actual chat text
|
|
// Now build the string we will be displaying to the user:
|
|
string sChatString = "<" + GetPlayerName(e.Message.SenderID) + "> " + sChatText;
|
|
// Now update our text
|
|
lock(txtChat)
|
|
{
|
|
if (txtChat.Text.Length > (txtChat.MaxLength * 0.95))
|
|
txtChat.Text = txtChat.Text.Remove(0, (int)(txtChat.MaxLength / 2));
|
|
|
|
txtChat.AppendText(sChatString);
|
|
txtChat.AppendText("\r\n");
|
|
txtChat.SelectionStart = txtChat.Text.Length;
|
|
txtChat.ScrollToCaret();
|
|
}
|
|
}
|
|
e.Message.ReceiveData.Dispose(); // We no longer need the data, Dispose the buffer
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// The session was terminated
|
|
/// </summary>
|
|
private void SessionTerminated(object sender, SessionTerminatedEventArgs e)
|
|
{
|
|
// Well, this session is being terminated, let the user know
|
|
if (e.Message.ResultCode == ResultCode.HostTerminatedSession)
|
|
MessageBox.Show("The Host has terminated this session. This sample will now exit.", "Exiting", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
else
|
|
MessageBox.Show("The session has been lost. This sample will now exit.", "Exiting", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
|
|
// This will post a message on the main thread to shut down our form
|
|
this.BeginInvoke(new PeerCloseCallback(this.PeerClose));
|
|
}
|
|
#endregion
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Exit the application
|
|
/// </summary>
|
|
private void btnExit_Click(object sender, System.EventArgs e)
|
|
{
|
|
// Exit the application
|
|
this.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This will return a players name based on the ID of that player
|
|
/// </summary>
|
|
private string GetPlayerName(int idPlayer)
|
|
{
|
|
lock (playerList)
|
|
{
|
|
foreach (Players p in playerList)
|
|
{
|
|
if (p.playerId == idPlayer)
|
|
return p.Name;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Fired when the text to send has been changed
|
|
/// </summary>
|
|
private void SendTextChanged(object sender, System.EventArgs e)
|
|
{
|
|
btnSend.Enabled = (((TextBox)sender).Text.Length > 0);
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// We want to send our chat message
|
|
/// </summary>
|
|
private void btnSend_Click(object sender, System.EventArgs e)
|
|
{
|
|
// Ok, we need to package up the text into a format
|
|
// that the c++ version of the sample expects since
|
|
// we are interop'ing with it.
|
|
int strlen = System.Text.Encoding.ASCII.GetByteCount(txtSend.Text);
|
|
NetworkPacket data = new NetworkPacket();
|
|
byte[] stringdata = new byte[strlen]; // Create our buffer
|
|
data.Write(ChatMessageId); // Set the msg type
|
|
//Now fill up the rest of the byte array with the ASCII chars of the string
|
|
System.Text.Encoding.ASCII.GetBytes(txtSend.Text, 0, strlen, stringdata, 0);
|
|
data.Write(stringdata);
|
|
// Now we've got the data setup, send it off.
|
|
peerObject.SendTo((int)PlayerID.AllPlayers, data, 0, SendFlags.Guaranteed);
|
|
// Now that we've sent out the text, clear it
|
|
txtSend.Text = null;
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Shut down the sample
|
|
/// </summary>
|
|
public void PeerClose()
|
|
{
|
|
// The session was terminated, go ahead and shut down
|
|
this.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The main entry point for the application.
|
|
/// </summary>
|
|
static void Main()
|
|
{
|
|
using (ChatPeer wfData = new ChatPeer())
|
|
{
|
|
try
|
|
{
|
|
Application.Run(wfData);
|
|
}
|
|
catch{}
|
|
}
|
|
}
|
|
}
|
|
}
|