601 lines
22 KiB
C++
601 lines
22 KiB
C++
//
|
|
// Copyright 2020 Electronic Arts Inc.
|
|
//
|
|
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
|
|
// software: you can redistribute it and/or modify it under the terms of
|
|
// the GNU General Public License as published by the Free Software Foundation,
|
|
// either version 3 of the License, or (at your option) any later version.
|
|
|
|
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
|
|
// in the hope that it will be useful, but with permitted additional restrictions
|
|
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
|
|
// distributed with this program. You should have received a copy of the
|
|
// GNU General Public License along with permitted additional restrictions
|
|
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* Project Name : Command & Conquer *
|
|
* *
|
|
* File Name : CCTEN.CPP *
|
|
* *
|
|
* Programmer : Bill R. Randolph *
|
|
* *
|
|
* Start Date : 01/09/96 *
|
|
* *
|
|
* Last Update : November 27, 1996 [BRR] *
|
|
* *
|
|
*-------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* Init_TEN -- Performs TEN-specific initialization *
|
|
* Shutdown_TEN -- Shuts down TEN connections *
|
|
* Connect_TEN -- Waits for connections to other players *
|
|
* Destroy_TEN_Connection -- Destroys the given connection *
|
|
* Debug_Mono -- Custom mono prints *
|
|
* Send_TEN_Win_Packet -- Sends a win packet to server *
|
|
* Send_TEN_Alliance -- Sends an ally/enemy packet to server *
|
|
* Send_TEN_Out_Of_Sync -- Announces this game out of sync *
|
|
* Send_TEN_Packet_Too_Late -- Announces packet-received-too-late *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
#include "function.h"
|
|
|
|
#if(TEN)
|
|
#ifdef WIN32
|
|
#define WINDOWS
|
|
#endif
|
|
#include "ten.h"
|
|
#endif
|
|
|
|
void Connect_TEN(void);
|
|
void Debug_Mono(void);
|
|
|
|
/***************************************************************************
|
|
* Init_TEN -- Performs TEN-specific initialization *
|
|
* *
|
|
* INPUT: *
|
|
* none. *
|
|
* *
|
|
* OUTPUT: *
|
|
* 1 = OK, 0 = error *
|
|
* *
|
|
* WARNINGS: *
|
|
* none. *
|
|
* *
|
|
* HISTORY: *
|
|
* 01/09/1996 BRR : Created. *
|
|
*=========================================================================*/
|
|
int Init_TEN(void)
|
|
{
|
|
#if(TEN)
|
|
//------------------------------------------------------------------------
|
|
// Allocate a packet buffer for TEN's use
|
|
//------------------------------------------------------------------------
|
|
Session.TenPacket = new char[Session.TenSize];
|
|
|
|
//------------------------------------------------------------------------
|
|
// Read the multiplayer settings from the CONQUER.INI file, and the game
|
|
// options from the options file.
|
|
//------------------------------------------------------------------------
|
|
Session.Read_MultiPlayer_Settings();
|
|
|
|
if (!Read_TEN_Game_Options()) {
|
|
WWMessageBox().Process("Unable to load game settings!");
|
|
//Prog_End();
|
|
Emergency_Exit(0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Flush all incoming packets
|
|
//------------------------------------------------------------------------
|
|
Ten->Flush_All();
|
|
|
|
//------------------------------------------------------------------------
|
|
// Form connections to all other players
|
|
//------------------------------------------------------------------------
|
|
Connect_TEN();
|
|
|
|
//------------------------------------------------------------------------
|
|
// Set multiplayer values for the local system, and timing values.
|
|
//------------------------------------------------------------------------
|
|
Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP;
|
|
|
|
return (1);
|
|
#else
|
|
return (1);
|
|
#endif
|
|
|
|
} // end of Init_TEN
|
|
|
|
|
|
/***************************************************************************
|
|
* Shutdown_TEN -- Shuts down TEN connections *
|
|
* *
|
|
* INPUT: *
|
|
* none. *
|
|
* *
|
|
* OUTPUT: *
|
|
* none. *
|
|
* *
|
|
* WARNINGS: *
|
|
* none. *
|
|
* *
|
|
* HISTORY: *
|
|
* 01/09/1996 BRR : Created. *
|
|
*=========================================================================*/
|
|
void Shutdown_TEN(void)
|
|
{
|
|
#if(TEN)
|
|
CDTimerClass<SystemTimerClass> timer;
|
|
|
|
//------------------------------------------------------------------------
|
|
// Wait a full second before exiting, to ensure all packets get sent.
|
|
//------------------------------------------------------------------------
|
|
timer = 60;
|
|
while (timer) ;
|
|
|
|
//------------------------------------------------------------------------
|
|
// Free memory
|
|
//------------------------------------------------------------------------
|
|
if (Session.TenPacket) {
|
|
delete [] Session.TenPacket;
|
|
Session.TenPacket = NULL;
|
|
}
|
|
|
|
if (Ten) {
|
|
delete Ten;
|
|
Ten = NULL;
|
|
}
|
|
|
|
return;
|
|
|
|
#endif
|
|
} // end of Shutdown_TEN
|
|
|
|
|
|
/***************************************************************************
|
|
* Connect_TEN -- Waits for connections to other players *
|
|
* *
|
|
* INPUT: *
|
|
* none. *
|
|
* *
|
|
* OUTPUT: *
|
|
* none. *
|
|
* *
|
|
* WARNINGS: *
|
|
* MPlayerCount must have been initialized at this point. *
|
|
* *
|
|
* HISTORY: *
|
|
* 01/10/1996 BRR : Created. *
|
|
*=========================================================================*/
|
|
void Connect_TEN(void)
|
|
{
|
|
#if(TEN)
|
|
typedef struct ConnectPacketTag {
|
|
NetCommandType Dummy; // packet type; set to PING
|
|
char Name[MPLAYER_NAME_MAX]; // player's name
|
|
HousesType House; // player's ActLike
|
|
unsigned char Color; // player's Color
|
|
} ConnectPacketType;
|
|
int num_players;
|
|
int num_found;
|
|
ConnectPacketType send_packet;
|
|
ConnectPacketType receive_packet;
|
|
int address;
|
|
int found;
|
|
int size;
|
|
int i;
|
|
CDTimerClass<SystemTimerClass> send_timer;
|
|
NodeNameType *who;
|
|
|
|
enum {
|
|
D_TXT6_H = 7,
|
|
D_MARGIN = 5,
|
|
};
|
|
static int x,y,w,h;
|
|
char const *buf1;
|
|
char const *buf2;
|
|
|
|
int display = 0;
|
|
RemapControlType * scheme = GadgetClass::Get_Color_Scheme();
|
|
|
|
//
|
|
// Clear the Players list
|
|
//
|
|
while (Session.Players.Count() > 0) {
|
|
delete Session.Players[0];
|
|
Session.Players.Delete(Session.Players[0]);
|
|
}
|
|
|
|
//
|
|
// Add myself to the list first thing
|
|
//
|
|
who = new NodeNameType;
|
|
strcpy(who->Name, Session.Handle);
|
|
who->Player.House = Session.House;
|
|
who->Player.Color = Session.ColorIdx;
|
|
Session.Players.Add (who);
|
|
|
|
//
|
|
// Find out how many players to wait for
|
|
//
|
|
num_players = Session.NumPlayers - 1;
|
|
num_found = 0;
|
|
|
|
//
|
|
// Send out a packet announcing my presence
|
|
//
|
|
send_packet.Dummy = NET_PING;
|
|
strcpy(send_packet.Name, Session.Handle);
|
|
send_packet.House = Session.House;
|
|
send_packet.Color = Session.ColorIdx;
|
|
Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 0, -1);
|
|
|
|
//
|
|
// Start our packet-sending timer
|
|
//
|
|
send_timer = 240;
|
|
|
|
//
|
|
// Wait for all players to enter the game
|
|
//
|
|
display = 1;
|
|
while (num_found < num_players) {
|
|
|
|
#ifdef WIN32
|
|
/*
|
|
** If we have just received input focus again after running in the background then
|
|
** we need to redraw.
|
|
*/
|
|
if (AllSurfaces.SurfacesRestored) {
|
|
AllSurfaces.SurfacesRestored=FALSE;
|
|
display = 1;
|
|
}
|
|
#endif
|
|
|
|
if (display) {
|
|
Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT);
|
|
buf1 = Text_String(TXT_WAITING_FOR_CONNECTIONS);
|
|
buf2 = Text_String(TXT_PRESS_ESC);
|
|
w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2));
|
|
w += (D_MARGIN * 2);
|
|
h = (D_TXT6_H * 2) + (D_MARGIN * 7);
|
|
x = 160 - (w / 2);
|
|
y = 100 - (h / 2);
|
|
Hide_Mouse();
|
|
//Set_Logic_Page(SeenBuff);
|
|
Dialog_Box(x * RESFACTOR, y * RESFACTOR, w * RESFACTOR, h * RESFACTOR);
|
|
|
|
Fancy_Text_Print(buf1,
|
|
160 * RESFACTOR,
|
|
(y + (D_MARGIN * 2)) * RESFACTOR,
|
|
scheme, TBLACK, TPF_CENTER | TPF_TEXT);
|
|
Fancy_Text_Print(buf2,
|
|
160 * RESFACTOR,
|
|
(y + (D_MARGIN * 2) + D_TXT6_H + D_MARGIN) * RESFACTOR,
|
|
scheme, TBLACK, TPF_CENTER | TPF_TEXT);
|
|
Show_Mouse();
|
|
display = 0;
|
|
}
|
|
|
|
Ten->Service();
|
|
|
|
//
|
|
// Check for an incoming packet; if a PING comes in, see if we already
|
|
// have this player in our Players list. If not, add him.
|
|
//
|
|
if (Ten->Get_Global_Message (&receive_packet, &size, &address) &&
|
|
receive_packet.Dummy == NET_PING) {
|
|
found = 0;
|
|
for (i = 1; i < Session.Players.Count(); i++) {
|
|
if (Session.Players[i]->TenAddress == address) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create a new connection and a new node in the list.
|
|
//
|
|
if (!found) {
|
|
|
|
who = new NodeNameType;
|
|
strcpy(who->Name, receive_packet.Name);
|
|
who->TenAddress = address;
|
|
who->Player.House = receive_packet.House;
|
|
who->Player.Color = (PlayerColorType)receive_packet.Color;
|
|
Session.Players.Add (who);
|
|
|
|
num_found++;
|
|
|
|
Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 1,
|
|
address);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the user hits ESC, bail out.
|
|
//
|
|
if (Keyboard->Check()) {
|
|
if (Keyboard->Get() == KN_ESC) {
|
|
//Prog_End();
|
|
Emergency_Exit(0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// When our timer expires, re-send the packet.
|
|
//
|
|
if (!send_timer) {
|
|
send_packet.Dummy = NET_PING;
|
|
Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 0, -1);
|
|
send_timer = 240;
|
|
}
|
|
}
|
|
|
|
#else
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* Destroy_TEN_Connection -- Destroys the given connection *
|
|
* *
|
|
* INPUT: *
|
|
* id connection ID to destroy (must be a HousesType) *
|
|
* error 0 = user signed off; 1 = connection error; otherwise, *
|
|
* no error is shown. *
|
|
* *
|
|
* OUTPUT: *
|
|
* none. *
|
|
* *
|
|
* WARNINGS: *
|
|
* none. *
|
|
* *
|
|
* HISTORY: *
|
|
* 01/11/1996 BRR : Created. *
|
|
*=========================================================================*/
|
|
void Destroy_TEN_Connection(int id, int error)
|
|
{
|
|
#if(TEN)
|
|
int i;
|
|
HouseClass *housep;
|
|
char txt[80];
|
|
|
|
//------------------------------------------------------------------------
|
|
// Do nothing if the house isn't human.
|
|
//------------------------------------------------------------------------
|
|
housep = HouseClass::As_Pointer((HousesType)id);
|
|
if (!housep || !housep->IsHuman)
|
|
return;
|
|
|
|
/*------------------------------------------------------------------------
|
|
Create a message to display to the user
|
|
------------------------------------------------------------------------*/
|
|
txt[0] = '\0';
|
|
if (error==1) {
|
|
sprintf(txt,Text_String(TXT_CONNECTION_LOST),Ten->Connection_Name(id));
|
|
} else if (error==0) {
|
|
sprintf(txt,Text_String(TXT_LEFT_GAME),Ten->Connection_Name(id));
|
|
}
|
|
|
|
if (strlen(txt)) {
|
|
Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor,
|
|
TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE);
|
|
Map.Flag_To_Redraw(false);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Remove this player from the Players vector
|
|
//------------------------------------------------------------------------
|
|
for (i = 0; i < Session.Players.Count(); i++) {
|
|
if (!stricmp(Session.Players[i]->Name,housep->IniName)) {
|
|
delete Session.Players[i];
|
|
Session.Players.Delete(Session.Players[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------
|
|
Delete the TEN connection
|
|
------------------------------------------------------------------------*/
|
|
Ten->Delete_Connection(id);
|
|
|
|
//------------------------------------------------------------------------
|
|
// Turn the player's house over to the computer's AI
|
|
//------------------------------------------------------------------------
|
|
housep->IsHuman = false;
|
|
housep->IQ = Rule.MaxIQ;
|
|
strcpy (housep->IniName,Text_String(TXT_COMPUTER));
|
|
|
|
Session.NumPlayers--;
|
|
|
|
/*------------------------------------------------------------------------
|
|
If we're the last player left, tell the user.
|
|
------------------------------------------------------------------------*/
|
|
if (Session.NumPlayers == 1) {
|
|
sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME));
|
|
Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor,
|
|
TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE);
|
|
Map.Flag_To_Redraw(false);
|
|
}
|
|
|
|
#else
|
|
id = id;
|
|
error = error;
|
|
|
|
#endif
|
|
} // end of Destroy_TEN_Connection
|
|
|
|
|
|
/***************************************************************************
|
|
* Debug_Mono -- Custom mono prints *
|
|
* *
|
|
* INPUT: *
|
|
* none. *
|
|
* *
|
|
* OUTPUT: *
|
|
* none. *
|
|
* *
|
|
* WARNINGS: *
|
|
* none. *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/27/1996 BRR : Created. *
|
|
*=========================================================================*/
|
|
void Debug_Mono(void)
|
|
{
|
|
#if(TEN)
|
|
int i;
|
|
int id;
|
|
|
|
Mono_Printf("STATE: # Connections:%d\n",Ten->Num_Connections());
|
|
for (i=0;i<Ten->Num_Connections();i++) {
|
|
id = Ten->Connection_ID(i);
|
|
Mono_Printf("Connection %d: Name:%s, ID:%d, Address:%d\n",
|
|
i,
|
|
Ten->Connection_Name(id),
|
|
Ten->Connection_ID(i),
|
|
Ten->Connection_Address(id));
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* Send_TEN_Win_Packet -- Sends a win packet to server *
|
|
* *
|
|
* INPUT: *
|
|
* none. *
|
|
* *
|
|
* OUTPUT: *
|
|
* none. *
|
|
* *
|
|
* WARNINGS: *
|
|
* none. *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/27/1996 BRR : Created. *
|
|
*=========================================================================*/
|
|
void Send_TEN_Win_Packet(void)
|
|
{
|
|
#if(TEN)
|
|
char winbuf[80];
|
|
char idbuf[20];
|
|
int first = 1;
|
|
HouseClass *hptr;
|
|
int i;
|
|
|
|
//
|
|
// Build a special text buffer to send to the TEN server. Format:
|
|
// "winner 'id id'", where 'id' is the Ten Player ID of each player
|
|
// on the winning team. (For TEN, the color index is the player ID.)
|
|
//
|
|
sprintf(winbuf,"winner '");
|
|
for (i = 0; i < Session.Players.Count(); i++) {
|
|
hptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
|
|
if (!hptr->IsDefeated) {
|
|
if (!first) {
|
|
strcat(winbuf," ");
|
|
} else {
|
|
first = 0;
|
|
}
|
|
sprintf(idbuf,"%d", Session.Players[i]->Player.Color);
|
|
strcat (winbuf, idbuf);
|
|
}
|
|
}
|
|
strcat (winbuf,"' ");
|
|
tenArSetPlayerState(winbuf);
|
|
#endif // TEN
|
|
|
|
} // end of Send_TEN_Win_Packet
|
|
|
|
|
|
/***************************************************************************
|
|
* Send_TEN_Alliance -- Sends an ally/enemy packet to server *
|
|
* *
|
|
* INPUT: *
|
|
* whom name of player we're allying / enemying with *
|
|
* ally 1 = we're allying; 0 = we're breaking the alliance *
|
|
* *
|
|
* OUTPUT: *
|
|
* none. *
|
|
* *
|
|
* WARNINGS: *
|
|
* none. *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/27/1996 BRR : Created. *
|
|
*=========================================================================*/
|
|
void Send_TEN_Alliance(char *whom, int ally)
|
|
{
|
|
#if(TEN)
|
|
char buf[80];
|
|
|
|
if (ally) {
|
|
sprintf(buf,"ally '%s' ",whom);
|
|
} else {
|
|
sprintf(buf,"enemy '%s' ",whom);
|
|
}
|
|
|
|
tenArSetPlayerState(buf);
|
|
|
|
#else
|
|
whom = whom;
|
|
ally = ally;
|
|
#endif // TEN
|
|
|
|
} // end of Send_TEN_Alliance
|
|
|
|
|
|
/***************************************************************************
|
|
* Send_TEN_Out_Of_Sync -- Announces this game out of sync *
|
|
* *
|
|
* INPUT: *
|
|
* none. *
|
|
* *
|
|
* OUTPUT: *
|
|
* none. *
|
|
* *
|
|
* WARNINGS: *
|
|
* none. *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/27/1996 BRR : Created. *
|
|
*=========================================================================*/
|
|
void Send_TEN_Out_Of_Sync(void)
|
|
{
|
|
#if(TEN)
|
|
tenArSetPlayerState("sync '1' ");
|
|
#endif // TEN
|
|
|
|
} // end of Send_TEN_Out_Of_Sync
|
|
|
|
|
|
/***************************************************************************
|
|
* Send_TEN_Packet_Too_Late -- Announces packet-received-too-late *
|
|
* *
|
|
* INPUT: *
|
|
* none. *
|
|
* *
|
|
* OUTPUT: *
|
|
* none. *
|
|
* *
|
|
* WARNINGS: *
|
|
* none. *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/27/1996 BRR : Created. *
|
|
*=========================================================================*/
|
|
void Send_TEN_Packet_Too_Late(void)
|
|
{
|
|
#if(TEN)
|
|
tenArSetPlayerState("toolate '1' ");
|
|
#endif // TEN
|
|
|
|
} // end of Send_TEN_Packet_Too_Late
|
|
|
|
|
|
/***************************** end of ccten.cpp *****************************/
|