| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555 |
- /*
- ** Command & Conquer Red Alert(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program 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.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /* $Header: /counterstrike/QUEUE.CPP 6 3/14/97 5:12p Steve_tall $ */
- /***************************************************************************
- *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
- * *
- * Project Name : Command & Conquer *
- * *
- * File Name : QUEUE.CPP *
- * *
- * Programmer : Bill R. Randolph *
- * *
- * Start Date : 11/28/95 *
- * *
- * Last Update : October 14, 1996 [BRR] *
- * *
- *-------------------------------------------------------------------------*
- * Functions for Queueing Events: *
- * Queue_Mission -- Queue a mega mission event. *
- * Queue_Options -- Queue the options event. *
- * Queue_Exit -- Add the exit game event to the queue. *
- * *
- * Functions for processing Queued Events: *
- * Queue_AI -- Process all queued events. *
- * Queue_AI_Normal -- Process all queued events. *
- * Queue_AI_Multiplayer -- Process all queued events. *
- * *
- * Main Multiplayer Queue Logic: *
- * Wait_For_Players -- Waits for other systems to come on-line *
- * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event *
- * Process_Send_Period -- timing for sending packets every 'n' frames *
- * Send_Packets -- sends out events from the OutList *
- * Send_FrameSync -- Sends a FRAMESYNC packet *
- * Process_Receive_Packet -- processes an incoming packet *
- * Process_Serial_Packet -- Handles an incoming serial packet *
- * Can_Advance -- determines if it's OK to advance to the next frame *
- * Process_Reconnect_Dialog -- processes the reconnection dialog *
- * Handle_Timeout -- attempts to reconnect; if fails, bails. *
- * Stop_Game -- stops the game *
- * *
- * Packet Compression / Decompression: *
- * Build_Send_Packet -- Builds a big packet from a bunch of little ones. *
- * Add_Uncompressed_Events -- adds uncompressed events to a packet *
- * Add_Compressed_Events -- adds compressed events to a packet *
- * Breakup_Receive_Packet -- Splits a big packet into little ones. *
- * Extract_Uncompressed_Events -- extracts events from a packet *
- * Extract_Compressed_Events -- extracts events from a packet *
- * *
- * DoList Management: *
- * Execute_DoList -- Executes commands from the DoList *
- * Clean_DoList -- Cleans out old events from the DoList *
- * Queue_Record -- Records the DoList to disk *
- * Queue_Playback -- plays back queue entries from a record file *
- * *
- * Debugging: *
- * Compute_Game_CRC -- Computes a CRC value of the entire game. *
- * Add_CRC -- Adds a value to a CRC *
- * Print_CRCs -- Prints a data file for finding Sync Bugs *
- * Init_Queue_Mono -- inits mono display *
- * Update_Queue_Mono -- updates mono display *
- * Print_Framesync_Values -- displays frame-sync variables *
- * Check_Mirror -- Checks mirror memory *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "function.h"
- #ifdef WOLAPI_INTEGRATION
- //#include "WolDebug.h"
- #include "WolapiOb.h"
- extern WolapiObject* pWolapi;
- bool bReconnectDialogCancelled;
- #endif
- /********************************** Defines *********************************/
- #define SHOW_MONO 0
- /********************************** Globals *********************************/
- //---------------------------------------------------------------------------
- // GameCRC is the current computed CRC value for this frame.
- // CRC[] is a record of our last 32 game CRC's.
- // ColorNames is for debug output in Print_CRCs
- //---------------------------------------------------------------------------
- static unsigned long GameCRC;
- static unsigned long CRC[32] =
- {0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,
- 0,0};
- static char *ColorNames[8] = {
- "Yellow",
- "LtBlue",
- "Red",
- "Green",
- "Orange",
- "Grey",
- "Blue",
- "Brown"
- };
- //...........................................................................
- // Mono debugging variables:
- // NetMonoMode: 0 = show connection output, 1 = flowcount output
- // NewMonoMode: set by anything that toggles NetMonoMode; re-inits screen
- // IsMono: used for taking control of Mono screen away from the engine
- //...........................................................................
- int NetMonoMode = 1;
- int NewMonoMode = 1;
- static int IsMono = 0;
- //---------------------------------------------------------------------------
- // Several routines return various codes; here's an enum for all of them.
- //---------------------------------------------------------------------------
- typedef enum RetcodeEnum {
- RC_NORMAL, // no news is good news
- RC_PLAYER_READY, // a new player has been heard from
- RC_SCENARIO_MISMATCH, // scenario mismatch
- RC_DOLIST_FULL, // DoList is full
- RC_SERIAL_PROCESSED, // modem: SERIAL packet was processed
- RC_PLAYER_LEFT, // modem: other player left the game
- RC_HUNG_UP, // modem has hung up
- RC_NOT_RESPONDING, // other player not responding (timeout/hung up)
- RC_CANCEL, // user cancelled
- } RetcodeType;
- #ifdef FIXIT_CSII // checked - ajw 9/28/98
- extern void Enable_Secret_Units(void);
- #endif
- /********************************* Prototypes *******************************/
- //...........................................................................
- // Main multiplayer queue logic
- //...........................................................................
- static void Queue_AI_Normal(void);
- static void Queue_AI_Multiplayer(void);
- static RetcodeType Wait_For_Players(int first_time, ConnManClass *net,
- int resend_delta, int dialog_time, int timeout, char *multi_packet_buf,
- int my_sent, long *their_frame, unsigned short *their_sent,
- unsigned short *their_recv);
- static void Generate_Timing_Event(ConnManClass *net, int my_sent);
- static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent);
- static void Generate_Process_Time_Event(ConnManClass *net);
- static int Process_Send_Period(ConnManClass *net); //, int init);
- static int Send_Packets(ConnManClass *net, char *multi_packet_buf,
- int multi_packet_max, int max_ahead, int my_sent);
- static void Send_FrameSync(ConnManClass *net, int cmd_count);
- static RetcodeType Process_Receive_Packet(ConnManClass *net,
- char *multi_packet_buf, int id, int packetlen, long *their_frame,
- unsigned short *their_sent, unsigned short *their_recv);
- static RetcodeType Process_Serial_Packet(char *multi_packet_buf,
- int first_time);
- static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame,
- unsigned short *their_sent, unsigned short *their_recv);
- static int Process_Reconnect_Dialog(CDTimerClass<SystemTimerClass> *timeout_timer,
- long *their_frame, int num_conn, int reconn, int fresh);
- static int Handle_Timeout(ConnManClass *net, long *their_frame,
- unsigned short *their_sent, unsigned short *their_recv);
- static void Stop_Game(void);
- //...........................................................................
- // Packet compression/decompression:
- //...........................................................................
- static int Build_Send_Packet(void *buf, int bufsize, int frame_delay,
- int num_cmds, int cap);
- int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, int size,
- int cap);
- int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, int size,
- int cap);
- static int Breakup_Receive_Packet(void *buf, int bufsize );
- int Extract_Uncompressed_Events(void *buf, int bufsize);
- int Extract_Compressed_Events(void *buf, int bufsize);
- //...........................................................................
- // DoList management:
- //...........................................................................
- static int Execute_DoList(int max_houses, HousesType base_house,
- ConnManClass *net, CDTimerClass<FrameTimerClass> *skip_crc,
- // ConnManClass *net, TCountDownTimerClass *skip_crc,
- long *their_frame, unsigned short *their_sent, unsigned short *their_recv);
- static void Clean_DoList(ConnManClass *net);
- static void Queue_Record(void);
- static void Queue_Playback(void);
- //...........................................................................
- // Debugging:
- //...........................................................................
- static void Compute_Game_CRC(void);
- void Add_CRC(unsigned long *crc, unsigned long val);
- static void Print_CRCs(EventClass *ev);
- static void Init_Queue_Mono(ConnManClass *net);
- static void Update_Queue_Mono(ConnManClass *net, int flow_index);
- static void Print_Framesync_Values(long curframe, unsigned long max_ahead,
- int num_connections, unsigned short *their_recv,
- unsigned short *their_sent, unsigned short my_sent);
- extern void Keyboard_Process(KeyNumType &input);
- void Dump_Packet_Too_Late_Stuff(EventClass *event, ConnManClass *net,
- long *their_frame, unsigned short *their_sent, unsigned short *their_recv);
- void Check_Mirror(void);
- /***************************************************************************
- * Queue_Mission -- Queue a mega mission event. *
- * *
- * This routine is called when the player causes a change to a game unit. *
- * The event that initiates the change is queued to as a result of a call *
- * to this routine. *
- * *
- * INPUT: *
- * whom Whom this mission request applies to (a friendly unit). *
- * mission The mission to assign to this object. *
- * target The target of this mission (if any). *
- * dest The movement destination for this mission (if any). *
- * *
- * OUTPUT: *
- * Was the mission request queued successfully? *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 09/21/1995 JLB : Created. *
- *=========================================================================*/
- bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination)
- {
- if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination)))) {
- return(false);
- } else {
- return(true);
- }
- }
- /***********************************************************************************************
- * Queue_Mission -- Queue a mega mission event, formation override for common speed. *
- * *
- * This routine is called when the player causes a change to a game unit. The event that *
- * initiates the change is queued to as a result of a call to this routine. *
- * *
- * INPUT: whom -- Whom this mission request applies to (a friendly unit). *
- * *
- * mission -- The mission to assign to this object. *
- * *
- * target -- The target of this mission (if any). *
- * *
- * dest -- The movement destination for this mission (if any). *
- * *
- * speed -- The override speed for this unit. *
- * *
- * maxspeed -- The override maximum speed for this unit. *
- * *
- * OUTPUT: Was the mission request queued successfully? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 09/21/1995 JLB : Created. *
- *=============================================================================================*/
- bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination, SpeedType speed, MPHType maxspeed)
- {
- if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination), speed, maxspeed))) {
- return(false);
- } else {
- return(true);
- }
- }
- /***************************************************************************
- * Queue_Options -- Queue the options event. *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * Was the options screen event queued successfully? *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 09/21/1995 JLB : Created. *
- *=========================================================================*/
- bool Queue_Options(void)
- {
- if (! OutList.Add(EventClass(EventClass::OPTIONS))) {
- return(false);
- }
- else {
- return(true);
- }
- } /* end of Queue_Options */
- /***************************************************************************
- * Queue_Exit -- Add the exit game event to the queue. *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * Was the exit event queued successfully? *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 09/21/1995 JLB : Created. *
- *=========================================================================*/
- bool Queue_Exit(void)
- {
- if (! OutList.Add(EventClass(EventClass::EXIT))) {
- return(false);
- }
- else {
- return(true);
- }
- } /* end of Queue_Exit */
- /***************************************************************************
- * Queue_AI -- Process all queued events. *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 09/21/1995 JLB : Created. *
- *=========================================================================*/
- void Queue_AI(void)
- {
- if (Session.Play) {
- Queue_Playback();
- }
- else {
- switch (Session.Type) {
- case GAME_SKIRMISH:
- case GAME_NORMAL:
- Queue_AI_Normal();
- break;
- case GAME_MODEM:
- case GAME_NULL_MODEM:
- case GAME_IPX:
- case GAME_INTERNET:
- case GAME_TEN:
- case GAME_MPATH:
- Queue_AI_Multiplayer();
- break;
- }
- }
- } /* end of Queue_AI */
- /***************************************************************************
- * Queue_AI_Normal -- Process all queued events. *
- * *
- * This is the "normal" version of the queue management routine. It does *
- * the following: *
- * - Transfers items in the OutList to the DoList *
- * - Executes any commands in the DoList that are supposed to be done on *
- * this frame # *
- * - Cleans out the DoList *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 09/21/1995 JLB : Created. *
- *=========================================================================*/
- static void Queue_AI_Normal(void)
- {
- //------------------------------------------------------------------------
- // Move events from the OutList (events generated by this player) into the
- // DoList (the list of events to execute).
- //------------------------------------------------------------------------
- while (OutList.Count) {
- OutList.First().IsExecuted = false;
- if (!DoList.Add(OutList.First())) {
- ;
- }
- #ifdef MIRROR_QUEUE
- MirrorList.Add(OutList.First());
- #endif
- OutList.Next();
- }
- //------------------------------------------------------------------------
- // Save the DoList to disk, if we're in "Record" mode
- //------------------------------------------------------------------------
- if (Session.Record) {
- Queue_Record();
- }
- //------------------------------------------------------------------------
- // Execute the DoList; if an error occurs, bail out.
- //------------------------------------------------------------------------
- if (!Execute_DoList(1, PlayerPtr->Class->House, NULL, NULL, NULL,
- NULL, NULL)) {
- GameActive = 0;
- return;
- }
- //------------------------------------------------------------------------
- // Clean out the DoList
- //------------------------------------------------------------------------
- Clean_DoList(NULL);
- } /* end of Queue_AI_Normal */
- /***************************************************************************
- * Queue_AI_Multiplayer -- Process all queued events. *
- * *
- * This is the network version of the queue management routine. It does *
- * the following: *
- * - If this is the 1st frame, waits for other systems to signal ready *
- * - Generates a timing event, to allow the connection time to be dynamic *
- * - Handles timing related to sending packets every 'n' frames *
- * - Sends outgoing events *
- * - Frame-syncs to the other systems (see below) *
- * - Executes & cleans out the DoList *
- * *
- * The Frame-Sync'ing logic is the heart & soul of network play. It works *
- * by ensuring that any system won't out-run the other system by more than *
- * 'Session.MaxAhead' frames; this in turn ensures that a packet's *
- * execution frame # won't have been passed by the time that packet is *
- * received by all systems. *
- * *
- * To achieve this, the system must keep track of all other system's *
- * current frame #'s; these are stored in an array called 'their_frame[]'. *
- * However, because current frame #'s are sent in FRAMEINFO packets, which *
- * don't require an ACK, and command packets are sent in packets requiring *
- * an ACK, it's possible for a command packet to get lost, and the next *
- * frame's FRAMEINFO packet to not get lost; the other system may then *
- * advance past the frame # the command is to execute on! So, to prevent *
- * this, all FRAMEINFO packets include a CommandCount field. This value *
- * tells the other system how many events it should have received by this *
- * time. This system can therefore keep track of how many commands it's *
- * actually received, and compare it to the CommandCount field, to see if *
- * it's missed an event packet. The # of events we've received from each *
- * system is stored in 'their_recv[]', and the # events they say they've *
- * sent is stored in 'their_sent[]'. *
- * *
- * Thus, two conditions must be met in order to advance to the next frame: *
- * - Our current frame # must be < their_frame + Session.MaxAhead *
- * - their_recv[i] must be >= their_sent[i] *
- * *
- * 'their_frame[] is updated by Process_Receive_Packet() *
- * 'their_recv[] is updated by Process_Receive_Packet() *
- * 'their_sent[] is updated by Process_Receive_Packet() *
- * 'my_sent' is updated by this routine. *
- * *
- * The order of the arrays their_frame[] etc is the same order the *
- * connections are created in. The Sender's ID is passed to *
- * Connection_Index() to obtain the array index. *
- * *
- * The only routines allowed to pop up dialogs are: *
- * Wait_For_Players() (only pops up the reconnect dialog) *
- * Execute_DoList() (tells if out of sync, or packet recv'd too late) *
- * *
- * Sign-off's are detected by: *
- * - Timing out while waiting for a packet *
- * - Detecting that the other player is now at the score screen or *
- * connection dialog (serial) *
- * - If we see an EventClass::EXIT event on the private channel *
- * *
- * The current communications protocol, COMM_PROTOCOL_MULTI_E_COMP, has *
- * the following properties: *
- * - It compresses packets, so that the minimum number of bytes are *
- * transmitted. Packets are compressed by extracting all info common to *
- * the events into the packet header, and then sending only the bytes *
- * relevant to each type of event. For instance, if 100 infantry guys *
- * are told to move to the same location, the command itself & the *
- * location will be included in the 1st movement command only; after *
- * that, there will be a rep count then 99 infantry TARGET numbers, *
- * identifying all the infantry told to move. *
- * - The protocol also only sends packets out every 'n' frames. This cuts *
- * the data rate dramatically. It means that 'Session.MaxAhead' must be *
- * divisible by 'n'; also, the minimum value for 'Session.MaxAhead' is *
- * 'n * 2', to give both sides some "breathing" room in case a FRAMEINFO *
- * packet gets missed. *
- * *
- * Note: For synchronization-waiting loops (like waiting to hear from all *
- * other players, waiting to advance to the next frame, etc), use *
- * Net.Num_Connections() rather than Session.NumPlayers; this reflects the *
- * actual # of connections, and can be "faked" into playing even when *
- * there aren't any other players actually there. A typical example of *
- * this is playing back a recorded game. For command-execution loops, use *
- * Session.NumPlayers. This ensures all commands get executed, even if *
- * there isn't a human generating those commands. *
- * *
- * The modem works a little differently from the network in this respect: *
- * - The connection has to stay "alive" even if the other player exits to *
- * the join dialog. This prevents each system from timing out & hanging *
- * the modem up. Thus, packets are sent back & forth & just thrown away,*
- * but each system knows the other is still there. Messages may be sent *
- * between systems, though. *
- * - Destroy_Null_Connection doesn't hang up the modem, so *
- * Num_Connections() still reports a value of 1 even though the other *
- * player has left. *
- * - Any waits on Num_Connections() must also check for *
- * Session.NumPlayers > 1, to keep from waiting forever if the other *
- * guy has left *
- * - Packets sent to a player who's left require no ACK *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static void Queue_AI_Multiplayer(void)
- {
- if(Session.Type == GAME_SKIRMISH) return;
- //........................................................................
- // Enums:
- //........................................................................
- enum {
- MIXFILE_RESEND_DELTA = 120, // ticks b/w resends
- MIXFILE_TIMEOUT = 3600*2, // timeout waiting for mixfiles.
- FRAMESYNC_DLG_TIME = (3*60), // time until displaying reconnect dialog
- FRAMESYNC_TIMEOUT = (15*60), // timeout waiting for frame sync packet
- };
- int timeout_factor = (Session.Type == GAME_INTERNET) ? 6 : 1;
- //........................................................................
- // Variables for sending, receiving & parsing packets:
- //........................................................................
- ConnManClass *net; // ptr to access all multiplayer functions
- EventClass packet; // for sending single frame-sync's
- char *multi_packet_buf; // buffer for sending/receiving
- int multi_packet_max; // max length of multi_packet_buf
- //........................................................................
- // Frame-sync'ing variables
- //........................................................................
- static long
- their_frame[MAX_PLAYERS - 1]; // other players' frame #'s
- static unsigned short
- their_sent[MAX_PLAYERS - 1]; // # cmds other player claims to have sent
- static unsigned short
- their_recv[MAX_PLAYERS - 1]; // # cmds actually received from others
- static unsigned short
- my_sent; // # cmds I've sent out
- //........................................................................
- // Timing variables
- //........................................................................
- static CDTimerClass<FrameTimerClass> skip_crc; // to delay the CRC check
- // static TCountDownTimerClass skip_crc; // to delay the CRC check
- //........................................................................
- // Other misc variables
- //........................................................................
- int i;
- RetcodeType rc;
- int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed
- //------------------------------------------------------------------------
- // Initialize the packet buffer pointer & its max size
- //------------------------------------------------------------------------
- if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
- multi_packet_buf = NullModem.BuildBuf;
- multi_packet_max = NullModem.MaxLen - sizeof (CommHeaderType);
- net = &NullModem;
- } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
- multi_packet_buf = Session.MetaPacket;
- multi_packet_max = Session.MetaSize;
- net = &Ipx;
- }
- #if(TEN)
- else if (Session.Type == GAME_TEN) {
- multi_packet_buf = Session.TenPacket;
- multi_packet_max = Session.TenSize;
- net = Ten;
- }
- #endif
- #if(MPATH)
- else if (Session.Type == GAME_MPATH) {
- multi_packet_buf = Session.MPathPacket;
- multi_packet_max = Session.MPathSize;
- net = MPath;
- }
- #endif
- //------------------------------------------------------------------------
- // Debug stuff
- //------------------------------------------------------------------------
- Init_Queue_Mono(net);
- Update_Queue_Mono (net, 0);
- //------------------------------------------------------------------------
- // Compute the Game's CRC
- //------------------------------------------------------------------------
- Compute_Game_CRC();
- CRC[Frame & 0x001f] = GameCRC;
- //------------------------------------------------------------------------
- // If we've just started a game, or loaded a multiplayer game, we must
- // wait for all other systems to signal ready.
- //------------------------------------------------------------------------
- if (Frame==0 || Session.LoadGame) {
- //.....................................................................
- // Initialize static locals
- //.....................................................................
- for (i = 0; i < MAX_PLAYERS - 1; i++) {
- their_frame[i] = -1;
- their_sent[i] = 0;
- their_recv[i] = 0;
- }
- my_sent = 0;
- #ifdef FIXIT_MULTI_SAVE
- skip_crc = 32;
- #else
- skip_crc = Frame + 32;
- #endif // FIXIT_MULTI_SAVE
- for (i = 0; i < 32; i++)
- CRC[i] = 0;
- //.....................................................................
- // If we've loaded a saved game:
- // - If this game was saved as the result of a lost connection, clear
- // the CRC value so it will always match the other system's
- // - Otherwise, use the GameCRC value, so we'll compare save-game files
- // rather than scenario INI files
- //.....................................................................
- if (Session.LoadGame) {
- if (Session.EmergencySave)
- ScenarioCRC = 0;
- else
- ScenarioCRC = GameCRC;
- }
- //.....................................................................
- // Send our initial FRAMESYNC packet
- //.....................................................................
- Send_FrameSync(net, my_sent);
- //.....................................................................
- // Wait for the other guys
- //.....................................................................
- rc = Wait_For_Players (1, net, MIXFILE_RESEND_DELTA, FRAMESYNC_DLG_TIME*timeout_factor,
- MIXFILE_TIMEOUT, multi_packet_buf, my_sent, their_frame,
- their_sent, their_recv);
- if (rc != RC_NORMAL) {
- #ifdef WIN32
- if (Session.Type == GAME_INTERNET){
- Register_Game_End_Time();
- }
- #endif //WIN32
- if (rc == RC_NOT_RESPONDING) {
- WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING);
- }
- else if (rc == RC_SCENARIO_MISMATCH) {
- WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH);
- }
- else if (rc == RC_DOLIST_FULL) {
- WWMessageBox().Process(TXT_QUEUE_FULL);
- }
- Stop_Game();
- return;
- }
- //.....................................................................
- // Re-initialize frame numbers (in case somebody signed off while I was
- // waiting for MIX files to load; we would have fallen through, but
- // their frame # would still be -1).
- //.....................................................................
- for (i = 0; i < MAX_PLAYERS - 1; i++)
- their_frame[i] = 0;
- //.....................................................................
- // Reset the network response time computation, now that we're both
- // sending data again (loading MIX files will have introduced
- // deceptively large values).
- //.....................................................................
- net->Reset_Response_Time();
- //.....................................................................
- // Initialize the frame timers
- //.....................................................................
- if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
- Process_Send_Period(net);//, 1);
- }
- //.....................................................................
- // Turn off our special load-game flags
- //.....................................................................
- if (Session.LoadGame) {
- Session.EmergencySave = false;
- Session.LoadGame = false;
- }
- } // end of Frame 0 wait
- //------------------------------------------------------------------------
- // Adjust connection timing parameters every 128 frames.
- //------------------------------------------------------------------------
- else if ( (Frame & 0x007f) == 0) {
- //
- // If we're using the new spiffy protocol, do proper timing handling.
- // If we're the net "master", compute our desired frame rate & new
- // 'MaxAhead' value.
- //
- if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
- //
- // All systems will transmit their required process time.
- //
- Generate_Process_Time_Event(net);
- //
- // The game "host" will transmit timing adjustment events.
- //
- if (Session.Am_I_Master()) {
- Generate_Real_Timing_Event(net, my_sent);
- }
- } else {
- //
- // For the older protocols, do the old broken timing handling.
- //
- Generate_Timing_Event(net, my_sent);
- }
- }
- //------------------------------------------------------------------------
- // Only process every 'FrameSendRate' frames
- //------------------------------------------------------------------------
- if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
- if (!Process_Send_Period(net)) { //, 0)) {
- if (IsMono) {
- MonoClass::Disable();
- }
- return;
- }
- }
- //------------------------------------------------------------------------
- // Send our data packet(s); update my command-sent counter
- //------------------------------------------------------------------------
- my_sent += Send_Packets(net, multi_packet_buf, multi_packet_max,
- Session.MaxAhead, my_sent);
- //------------------------------------------------------------------------
- // If this is our first time through, we're done.
- //------------------------------------------------------------------------
- if (Frame==0) {
- if (IsMono) {
- MonoClass::Disable();
- }
- return;
- }
- //------------------------------------------------------------------------
- // Frame-sync'ing: wait until it's OK to advance to the next frame.
- //------------------------------------------------------------------------
- #ifdef FIXIT_VERSION_3
- int iFramesyncTimeout;
- if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.iPlayerCount > 2 )
- // Shortened resync timeout for non-2 player games.
- iFramesyncTimeout = 5 * 60; // One minute.
- else
- iFramesyncTimeout = FRAMESYNC_TIMEOUT;
- rc = Wait_For_Players (0, net,
- (Session.MaxAhead << 3),
- MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ),
- iFramesyncTimeout * (2*timeout_factor),
- multi_packet_buf, my_sent, their_frame,
- their_sent, their_recv);
- #else
- rc = Wait_For_Players (0, net,
- (Session.MaxAhead << 3),
- MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ),
- FRAMESYNC_TIMEOUT* (2*timeout_factor),
- multi_packet_buf, my_sent, their_frame,
- their_sent, their_recv);
- #endif
- if (rc != RC_NORMAL) {
- #ifdef WIN32
- if (Session.Type == GAME_INTERNET){
- Register_Game_End_Time();
- #ifdef WOLAPI_INTEGRATION
- // New rule - if you cancel a waiting to reconnect dialog, you lose.
- bReconnectDialogCancelled = ( rc == RC_CANCEL );
- #endif
- }
- #endif //WIN32
- if (rc == RC_NOT_RESPONDING) {
- WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING);
- }
- else if (rc == RC_SCENARIO_MISMATCH) {
- WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH);
- }
- else if (rc == RC_DOLIST_FULL) {
- WWMessageBox().Process(TXT_QUEUE_FULL);
- }
- Stop_Game();
- return;
- }
- //------------------------------------------------------------------------
- // Save the DoList to disk, if we're in "Record" mode
- //------------------------------------------------------------------------
- if (Session.Record) {
- Queue_Record();
- }
- //------------------------------------------------------------------------
- // Execute the DoList; if an error occurs, bail out.
- //------------------------------------------------------------------------
- if (!Execute_DoList(Session.MaxPlayers, HOUSE_MULTI1, net, &skip_crc,
- their_frame, their_sent, their_recv)) {
- #ifdef WIN32
- if (Session.Type == GAME_INTERNET){
- Register_Game_End_Time();
- }
- #endif //WIN32
- Stop_Game();
- return;
- }
- //------------------------------------------------------------------------
- // Clean out the DoList
- //------------------------------------------------------------------------
- Clean_DoList(net);
- if (IsMono) {
- MonoClass::Disable();
- }
- } // end of Queue_AI_Multiplayer
- /***************************************************************************
- * Wait_For_Players -- Waits for other systems to come on-line *
- * *
- * This routine performs the most critical logic in multiplayer; that of *
- * synchronizing my frame number with those of the other systems. *
- * *
- * INPUT: *
- * first_time 1 = 1st time this routine is called *
- * net ptr to connection manager *
- * resend_delta time (ticks) between FRAMESYNC resends *
- * dialog_time time (ticks) until pop up a reconnect dialog *
- * timeout time (ticks) until we give up the ghost *
- * multi_packet_buf buffer to store packets in *
- * my_sent # commands I've sent so far *
- * their_frame array of their frame #'s *
- * their_sent array of their CommandCount values *
- * their_recv array of # cmds I've received from them *
- * *
- * OUTPUT: *
- * RC_NORMAL OK to advance to the next frame *
- * RC_CANCEL user hit 'Cancel' at the timeout countdown dlg *
- * RC_NOT_RESPONDING other player(s) not responding *
- * RC_SCENARIO_MISMATCH scenario's don't match (first_time only) *
- * RC_DOLIST_FULL DoList was full *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static RetcodeType Wait_For_Players(int first_time, ConnManClass *net,
- int resend_delta, int dialog_time, int timeout, char *multi_packet_buf,
- int my_sent, long *their_frame, unsigned short *their_sent,
- unsigned short *their_recv)
- {
- //........................................................................
- // Variables for sending, receiving & parsing packets:
- //........................................................................
- EventClass *event; // event ptr for parsing incoming packets
- int packetlen; // size of meta-packet sent, & received
- int id; // id of other player
- int messages_this_loop; // to limit # messages processed each loop
- int message_limit; // max # messages we'll read each frame
- //........................................................................
- // Variables used only if 'first_time':
- //........................................................................
- int num_ready; // # players signalling ready
- //........................................................................
- // Timing variables
- //........................................................................
- CDTimerClass<SystemTimerClass> retry_timer; // time between FRAMESYNC packet resends
- CDTimerClass<SystemTimerClass> dialog_timer; // time to pop up a dialog
- CDTimerClass<SystemTimerClass> timeout_timer; // general-purpose timeout
- //........................................................................
- // Dialog variables
- //........................................................................
- int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed
- //........................................................................
- // Other misc variables
- //........................................................................
- KeyNumType input; // for user input
- int x,y; // for map input
- RetcodeType rc;
- //------------------------------------------------------------------------
- // Wait to hear from all other players
- //------------------------------------------------------------------------
- num_ready = 0;
- retry_timer = resend_delta; // time to retry
- dialog_timer = dialog_time; // time to show dlg
- timeout_timer = timeout; // time to bail out
- while (1) {
- Keyboard->Check();
- Update_Queue_Mono (net, 2);
- //---------------------------------------------------------------------
- // Resend a frame-sync packet if longer than one propagation delay goes
- // by; this prevents a "deadlock". If he's waiting for me to advance,
- // but has missed my last few FRAMEINFO packets, I may be waiting for
- // him to advance. Resending a FRAMESYNC ensures he knows what frame
- // number I'm on.
- //---------------------------------------------------------------------
- if (!retry_timer) {
- retry_timer = resend_delta; // time to retry
- Update_Queue_Mono (net, 3);
- Send_FrameSync(net, my_sent);
- }
- //---------------------------------------------------------------------
- // Service the connections
- //---------------------------------------------------------------------
- net->Service();
- //---------------------------------------------------------------------
- // Pop up a reconnect dialog if enough time goes by
- //---------------------------------------------------------------------
- if (!dialog_timer && SpecialDialog==SDLG_NONE) {
- if (reconnect_dlg == 0 && first_time == 0) {
- FILE *fp;
- int i;
- HouseClass *housep;
- fp = fopen("recon.txt","wt");
- if (fp) {
- fprintf(fp,"# Connections: %d\n",net->Num_Connections());
- fprintf(fp," My Frame #: %d\n",Frame);
- for (i = 0; i < net->Num_Connections(); i++) {
- housep = HouseClass::As_Pointer((HousesType)(net->Connection_ID(i)));
- fprintf(fp,"%15s: Their Sent:%d Their Recv:%d Their Frame:%d\n",
- housep->IniName, their_sent[i], their_recv[i], their_frame[i]);
- }
- fclose(fp);
- }
- #ifdef WOLAPI_INTEGRATION
- // "Reconnecting" dialog is about to be shown.
- // At this point, begin wolapi "disconnect pinging", if appropriate.
- if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament )
- pWolapi->Init_DisconnectPinging();
- #endif
- }
- if (Process_Reconnect_Dialog(&timeout_timer, their_frame, // (Returns immediately.)
- net->Num_Connections(), (first_time==0), (reconnect_dlg==0))) {
- return (RC_CANCEL);
- }
- reconnect_dlg = 1;
- #ifdef WOLAPI_INTEGRATION
- // Continue wolapi "disconnect pinging", if appropriate.
- if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->bDoingDisconnectPinging )
- pWolapi->Pump_DisconnectPinging();
- #endif
- }
- //---------------------------------------------------------------------
- // Exit if too much time goes by (the other system has crashed or
- // bailed)
- //---------------------------------------------------------------------
- if (!timeout_timer) {
- //..................................................................
- // For the first-time run, just give up; something's wrong.
- //..................................................................
- if (first_time) {
- return (RC_NOT_RESPONDING);
- }
- //..................................................................
- // Otherwise, we're in the middle of a game; so, the modem &
- // network must deal with a timeout differently.
- //..................................................................
- else {
- Update_Queue_Mono (net, 4);
- if (Handle_Timeout(net, their_frame, their_sent, their_recv)) {
- Map.Flag_To_Redraw(true); // erase modem reconnect dialog
- Map.Render();
- retry_timer = resend_delta;
- dialog_timer = dialog_time;
- timeout_timer = timeout;
- }
- #ifdef FIXIT_MULTI_SAVE
- #ifdef FIXIT_VERSION_3
- else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) ) {
- #else
- else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) &&
- PlayingAgainstVersion != VERSION_RED_ALERT_104) {
- #endif
- if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING,
- TXT_YES, TXT_NO, TXT_NONE) == 0) {
- Session.EmergencySave = 1;
- //printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC);
- //Print_CRCs(NULL);
- //printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n",
- // Scen.RandomNumber.Count1,
- // Scen.RandomNumber.Count2,
- // Scen.RandomNumber.Seed);
- Save_Game(-1, (char *)Text_String(TXT_MULTIPLAYER_GAME));
- //printf("After Save: Count1:%d, Count2:%d, Seed:%d\n",
- // Scen.RandomNumber.Count1,
- // Scen.RandomNumber.Count2,
- // Scen.RandomNumber.Seed);
- Session.EmergencySave = 0;
- }
- return (RC_CANCEL);
- }
- #endif // FIXIT_MULTI_SAVE
- else {
- return (RC_NOT_RESPONDING);
- }
- }
- }
- //---------------------------------------------------------------------
- // Check for an incoming message. We must still process commands
- // even if 'first_time' is set, in case the other system got my 1st
- // FRAMESYNC, but I didn't get his; he'll be at the next frame, and
- // may be sending commands.
- // We have to limit the number of incoming messages we handle; it's
- // possible to go into an infinite loop processing modem messages.
- // (This feature is disabled for Ten; we need to keep the TCP buffers
- // clear, so we read all the packets we can every time.)
- //---------------------------------------------------------------------
- messages_this_loop = 0;
- message_limit = 5;
- if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
- message_limit = 9999;
- }
- while ( (messages_this_loop++ < message_limit) &&
- net->Get_Private_Message (multi_packet_buf, &packetlen, &id) ) {
- Keyboard->Check();
- Update_Queue_Mono (net, 5);
- /*..................................................................
- Get an event ptr to the incoming message
- ..................................................................*/
- event = (EventClass *)multi_packet_buf;
- //------------------------------------------------------------------
- // Special processing for a modem game: process SERIAL packets
- //------------------------------------------------------------------
- if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
- rc = Process_Serial_Packet(multi_packet_buf, first_time);
- //...............................................................
- // SERIAL packet received & processed
- //...............................................................
- if (rc == RC_SERIAL_PROCESSED) {
- net->Service();
- retry_timer = resend_delta;
- dialog_timer = dialog_time;
- timeout_timer = timeout;
- continue;
- }
- //...............................................................
- // other player has left the game
- //...............................................................
- else if (rc == RC_PLAYER_LEFT) {
- if (first_time) {
- num_ready++;
- }
- break;
- }
- //...............................................................
- // Connection was lost
- //...............................................................
- else if (rc == RC_HUNG_UP) {
- #ifdef FIXIT_MULTI_SAVE
- #ifndef FIXIT_VERSION_3
- if (PlayingAgainstVersion != VERSION_RED_ALERT_104){
- #endif
- if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_HUNG_UP,
- TXT_YES, TXT_NO, TXT_NONE) == 0) {
- Session.EmergencySave = 1;
- //printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC);
- //Print_CRCs(NULL);
- //printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n",
- // Scen.RandomNumber.Count1,
- // Scen.RandomNumber.Count2,
- // Scen.RandomNumber.Seed);
- Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME));
- //printf("After Save: Count1:%d, Count2:%d, Seed:%d\n",
- // Scen.RandomNumber.Count1,
- // Scen.RandomNumber.Count2,
- // Scen.RandomNumber.Seed);
- Session.EmergencySave = 0;
- }
- return (RC_CANCEL);
- #ifndef FIXIT_VERSION_3
- }else{
- return (RC_NOT_RESPONDING);
- }
- #endif
- #else
- return (RC_NOT_RESPONDING);
- #endif // FIXIT_MULTI_SAVE
- }
- //...............................................................
- // If it was any other type of serial packet, break
- //...............................................................
- else if (rc != RC_NORMAL) {
- break;
- }
- }
- //------------------------------------------------------------------
- // Process the incoming packet
- //------------------------------------------------------------------
- rc = Process_Receive_Packet(net, multi_packet_buf, id, packetlen,
- their_frame, their_sent, their_recv);
- //..................................................................
- // New player heard from
- //..................................................................
- if (rc == RC_PLAYER_READY) {
- num_ready++;
- }
- //..................................................................
- // Scenario's don't match
- //..................................................................
- else if (rc == RC_SCENARIO_MISMATCH) {
- return (RC_SCENARIO_MISMATCH);
- }
- //..................................................................
- // DoList was full
- //..................................................................
- else if (rc == RC_DOLIST_FULL) {
- return (RC_DOLIST_FULL);
- }
- //..................................................................
- // Service the connection, to clean out the receive queues
- //..................................................................
- net->Service();
- }
- //---------------------------------------------------------------------
- // Debug output
- //---------------------------------------------------------------------
- Print_Framesync_Values(Frame, Session.MaxAhead, net->Num_Connections(),
- their_recv, their_sent, my_sent);
- //---------------------------------------------------------------------
- // Attempt to advance to the next frame.
- //---------------------------------------------------------------------
- //.....................................................................
- // For the first-time run, just check to see if we've heard from
- // everyone.
- //.....................................................................
- if (first_time) {
- if (num_ready >= net->Num_Connections()) {
- break;
- }
- }
- //.....................................................................
- // For in-game processing, we have to check their_sent, their_recv,
- // their_frame, etc.
- //.....................................................................
- else {
- if (Can_Advance(net, Session.MaxAhead, their_frame, their_sent,
- their_recv)) {
- break;
- }
- }
- //---------------------------------------------------------------------
- // Service game stuff. Servicing the map's input, and rendering the
- // map, allows the map to scroll even though we're hung up waiting for
- // packets. Don't do this if 'first_time' is set, since users could be
- // waiting a very long time for all systems to load the scenario, and
- // it gets frustrating being able to scroll around without doing
- // anything.
- //---------------------------------------------------------------------
- Call_Back();
- if (!first_time && SpecialDialog == SDLG_NONE && reconnect_dlg==0) {
- #ifdef WIN32
- WWMouse->Erase_Mouse(&HidPage, TRUE);
- #endif //WIN32
- Map.Input(input, x, y);
- if (input)
- Keyboard_Process(input);
- Map.Render();
- }
- } /* end of while */
- //------------------------------------------------------------------------
- // If the reconnect dialog was shown, force the map to redraw.
- //------------------------------------------------------------------------
- if (reconnect_dlg) {
- Map.Flag_To_Redraw(true);
- Map.Render();
- }
- return (RC_NORMAL);
- } // end of Wait_For_Players
- /***************************************************************************
- * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event *
- * *
- * This routine adjusts the connection timing on the local system; it also *
- * optionally generates a RESPONSE_TIME event, to tell all systems to *
- * dynamically adjust the current MaxAhead value. This allows both the *
- * MaxAhead & the connection retry logic to have dynamic timing, to adjust *
- * to varying line conditions. *
- * *
- * INPUT: *
- * net ptr to connection manager *
- * my_sent # commands I've sent out so far *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static void Generate_Timing_Event(ConnManClass *net, int my_sent)
- {
- unsigned long resp_time; // connection response time, in ticks
- EventClass ev;
- //
- // For now, TEN & MPATH don't measure the net's response time, so there's
- // no point in adjusting our timing. Do nothing.
- //
- if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
- return;
- }
- //------------------------------------------------------------------------
- // Measure the current connection response time. This time will be in
- // 60ths of a second, and represents full round-trip time of a packet.
- // To convert to one-way packet time, divide by 2; to convert to game
- // frames, divide again by 4, assuming a game rate of 15 fps.
- //------------------------------------------------------------------------
- resp_time = net->Response_Time();
- //------------------------------------------------------------------------
- // Adjust my connection retry timing; only do this if I've sent out more
- // than 5 commands, so I know I have a measure of the response time.
- //------------------------------------------------------------------------
- if (my_sent > 5) {
- net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
- //.....................................................................
- // If I'm the network "master", I'm also responsible for updating the
- // MaxAhead value on all systems, so do that here too.
- //.....................................................................
- if (Session.Am_I_Master()) {
- ev.Type = EventClass::RESPONSE_TIME;
- //..................................................................
- // For multi-frame compressed events, the MaxAhead must be an even
- // multiple of the FrameSendRate.
- //..................................................................
- if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
- ev.Data.FrameInfo.Delay = max( ((((resp_time / 8) +
- (Session.FrameSendRate - 1)) / Session.FrameSendRate) *
- Session.FrameSendRate), (Session.FrameSendRate * 2) );
- }
- //..................................................................
- // For sending packets every frame, just use the 1-way connection
- // response time.
- //..................................................................
- else {
- if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
- ev.Data.FrameInfo.Delay = max( (resp_time / 8),
- MODEM_MIN_MAX_AHEAD );
- } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
- ev.Data.FrameInfo.Delay = max( (resp_time / 8),
- NETWORK_MIN_MAX_AHEAD );
- }
- else if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
- ev.Data.FrameInfo.Delay = max( (resp_time / 8),
- MODEM_MIN_MAX_AHEAD );
- }
- }
- OutList.Add(ev);
- }
- }
- } // end of Generate_Timing_Event
- /***************************************************************************
- * Generate_Real_Timing_Event -- Generates a TIMING event *
- * *
- * INPUT: *
- * net ptr to connection manager *
- * my_sent # commands I've sent out so far *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 07/02/1996 BRR : Created. *
- *=========================================================================*/
- static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent)
- {
- unsigned long resp_time; // connection response time, in ticks
- EventClass ev;
- int highest_ticks;
- int i;
- int specified_frame_rate;
- int maxahead;
- //
- // If we haven't sent out at least 5 guaranteed-delivery packets, don't
- // bother trying to measure our connection response time; just return.
- //
- if (my_sent < 5) {
- return;
- }
- //
- // Find the highest processing time we have stored
- //
- highest_ticks = 0;
- for (i = 0; i < Session.Players.Count(); i++) {
- //
- // If we haven't heard from all systems yet, bail out.
- //
- if (Session.Players[i]->Player.ProcessTime == -1) {
- return;
- }
- if (Session.Players[i]->Player.ProcessTime > highest_ticks) {
- highest_ticks = Session.Players[i]->Player.ProcessTime;
- }
- }
- //
- // Compute our "desired" frame rate as the lower of:
- // - What the user has dialed into the options screen
- // - What we're really able to run at
- //
- if (highest_ticks == 0) {
- Session.DesiredFrameRate = 60;
- } else {
- Session.DesiredFrameRate = 60 / highest_ticks;
- }
- if (Options.GameSpeed == 0) {
- specified_frame_rate = 60;
- } else {
- specified_frame_rate = 60 / Options.GameSpeed;
- }
- Session.DesiredFrameRate = MIN (Session.DesiredFrameRate, specified_frame_rate);
- //
- // Measure the current connection response time. This time will be in
- // 60ths of a second, and represents full round-trip time of a packet.
- // To convert to one-way packet time, divide by 2; to convert to game
- // frames, ....uh....
- //
- resp_time = net->Response_Time();
- //
- // Compute our new 'MaxAhead' value, based upon the response time of our
- // connection and our desired frame rate.
- // 'MaxAhead' in frames is:
- //
- // (resp_time / 2 ticks) * (1 sec/60 ticks) * (n Frames / sec)
- //
- // resp_time is divided by 2 because, as reported, it represents a round-
- // trip, and we only want to use a one-way trip.
- //
- maxahead = (resp_time * Session.DesiredFrameRate) / (2 * 60);
- //
- // Now, we have to round 'maxahead' so it's an even multiple of our
- // send rate. It also must be at least thrice the FrameSendRate.
- // (Isn't "thrice" a cool word?)
- //
- maxahead = ((maxahead + Session.FrameSendRate - 1) / Session.FrameSendRate) * Session.FrameSendRate;
- maxahead = MAX (maxahead, Session.FrameSendRate * 3);
- ev.Type = EventClass::TIMING;
- ev.Data.Timing.DesiredFrameRate = Session.DesiredFrameRate;
- ev.Data.Timing.MaxAhead = maxahead;
- OutList.Add(ev);
- //
- // Adjust my connection retry timing. These values set the retry timeout
- // to just over one round-trip time, the 'maxretries' to -1, and the
- // connection timeout to allow for about 4 retries.
- //
- //net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
- if (Session.Type == GAME_INTERNET) {
- net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15);
- }else{
- net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
- }
- }
- /***************************************************************************
- * Generate_Process_Time_Event -- Generates a PROCESS_TIME event *
- * *
- * INPUT: *
- * net ptr to connection manager *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 07/02/1996 BRR : Created. *
- *=========================================================================*/
- static void Generate_Process_Time_Event(ConnManClass *net)
- {
- EventClass ev;
- int avgticks;
- unsigned long resp_time; // connection response time, in ticks
- //
- // Measure the current connection response time. This time will be in
- // 60ths of a second, and represents full round-trip time of a packet.
- // To convert to one-way packet time, divide by 2; to convert to game
- // frames, ....uh....
- //
- resp_time = net->Response_Time();
- //
- // Adjust my connection retry timing. These values set the retry timeout
- // to just over one round-trip time, the 'maxretries' to -1, and the
- // connection timeout to allow for about 4 retries.
- //
- //net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
- if (Session.Type == GAME_INTERNET) {
- net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15);
- }else{
- net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
- }
- if (IsMono) {
- MonoClass::Enable();
- Mono_Set_Cursor(0,23);
- Mono_Printf("Processing Ticks:%03d Frames:%03d\n", Session.ProcessTicks,Session.ProcessFrames);
- MonoClass::Disable();
- }
- avgticks = Session.ProcessTicks / Session.ProcessFrames;
- ev.Type = EventClass::PROCESS_TIME;
- ev.Data.ProcessTime.AverageTicks = avgticks;
- OutList.Add(ev);
- Session.ProcessTicks = 0;
- Session.ProcessFrames = 0;
- }
- /***************************************************************************
- * Process_Send_Period -- timing for sending packets every 'n' frames *
- * *
- * This function is for a CommProtocol of COMM_PROTOCOL_MULTI_E_COMP only. *
- * It determines if it's time to send a packet or not. *
- * *
- * INPUT: *
- * net ptr to connection manager *
- * *
- * OUTPUT: *
- * 1 = it's time to send a packet; 0 = don't send a packet this frame. *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static int Process_Send_Period(ConnManClass *net) //, int init)
- {
- //------------------------------------------------------------------------
- // If the current frame # is not an even multiple of 'FrameSendRate', then
- // it's not time to send a packet; just return.
- //------------------------------------------------------------------------
- if (Frame != (((Frame + (Session.FrameSendRate - 1)) /
- Session.FrameSendRate) * Session.FrameSendRate) ) {
- net->Service();
- if (IsMono) {
- MonoClass::Disable();
- }
- return (0);
- }
- return (1);
- } // end of Process_Send_Period
- /***************************************************************************
- * Send_Packets -- sends out events from the OutList *
- * *
- * This routine computes how many events can be sent this frame, and then *
- * builds the "meta-packet" & sends it. *
- * *
- * The 'cap' value is the max # of events we can send. Ideally, it should *
- * be based upon the bandwidth of our connection. Currently, it's just *
- * hardcoded to prevent the modem from having to resend "too much" data, *
- * which is about 200 bytes per frame. *
- * *
- * INPUT: *
- * net ptr to connection manager *
- * multi_packet_buf buffer to store packets in *
- * multi_packet_max max size of multi_packet_buf *
- * max_ahead current game MaxAhead value *
- * my_sent # commands I've sent this game *
- * *
- * OUTPUT: *
- * # events sent, NOT including the FRAMEINFO event *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static int Send_Packets(ConnManClass *net, char *multi_packet_buf,
- int multi_packet_max, int max_ahead, int my_sent)
- {
- int cap; // max # events to send, NOT including FRAMEINFO event
- int do_once; // true: only go through packet loop once
- int ack_req; // 0 = no ack required on outgoing packet
- int packetlen; // size of meta-packet sent
- //------------------------------------------------------------------------
- // Determine how many events it's OK to send this frame.
- //------------------------------------------------------------------------
- //........................................................................
- // If we have 4 or more packets queued for sending, don't add any more
- // this frame.
- //........................................................................
- if (net->Private_Num_Send() >= 4) {
- cap = 0;
- do_once = 1;
- }
- //........................................................................
- // If there are 2 or more packets queued, the entire packet we send must
- // fit within a single ComQueue buffer, so limit # events to 5.
- // (The Modem connection manager has a max buffer size of 200 bytes, which
- // is large enough for 6 uncompressed events, which leaves room for 5
- // events plus a FRAMEINFO.)
- //........................................................................
- else if (net->Private_Num_Send() >= 2) {
- cap = 5;
- do_once = 1;
- }
- //........................................................................
- // Otherwise, just send all events in the OutList
- //........................................................................
- else {
- cap = OutList.Count;
- do_once = 0;
- }
- //........................................................................
- // Make sure we aren't sending more events than are in the OutList
- //........................................................................
- if (cap > OutList.Count) {
- cap = OutList.Count;
- }
- //........................................................................
- // Make sure we don't send so many events that our DoList fills up
- //........................................................................
- if (cap > (MAX_EVENTS * 64) - DoList.Count) {
- cap = (MAX_EVENTS * 64) - DoList.Count;
- }
- //
- // 10/21/96 5:12PM - ST
- //
- if (Session.Type == GAME_INTERNET || Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM){
- cap = OutList.Count;
- do_once = 0;
- }
- //------------------------------------------------------------------------
- // Build our meta-packet & transmit it.
- //------------------------------------------------------------------------
- while (1) {
- Keyboard->Check();
- Update_Queue_Mono (net, 1);
- //.....................................................................
- // If there are no commands this frame, we'll just be sending a FRAMEINFO
- // packet; no ack is required. For the modem's sake, check
- // Session.NumPlayers; no ACK is needed if we're just sending to someone
- // who's left the game.
- //.....................................................................
- if (cap == 0 || OutList.Count == 0 || Session.NumPlayers == 1) {
- ack_req = 0;
- }
- else {
- ack_req = 1;
- }
- //.....................................................................
- // Build & send out our message
- //.....................................................................
- packetlen = Build_Send_Packet (multi_packet_buf, multi_packet_max,
- max_ahead, my_sent, cap);
- net->Send_Private_Message (multi_packet_buf, packetlen, ack_req);
- //.....................................................................
- // Call Service() to actually send the packet
- //.....................................................................
- net->Service();
- //.....................................................................
- // Stop if there's no more data to send, or if our send queue is
- // filling up.
- //.....................................................................
- if (OutList.Count == 0 || do_once) {
- break;
- }
- }
- return (cap);
- } // end of Send_Packets
- /***************************************************************************
- * Send_FrameSync -- Sends a FRAMESYNC packet *
- * *
- * This routine is used to periodically remind the other systems that *
- * we're still here, and to tell them what frame # we're on, in case *
- * they've missed my FRAMEINFO packets. *
- * *
- * INPUT: *
- * net ptr to connection manager *
- * cmd_count # commands I've sent so far *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static void Send_FrameSync(ConnManClass *net, int cmd_count)
- {
- EventClass packet;
- //------------------------------------------------------------------------
- // Build a frame-sync event to send. FRAMESYNC packets contain a
- // scenario-based CRC rather than a game-state-based CRC, to let the
- // games compare scenario CRC's on startup.
- //------------------------------------------------------------------------
- memset (&packet, 0, sizeof(EventClass));
- packet.Type = EventClass::FRAMESYNC;
- if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
- packet.Frame = ((Frame + Session.MaxAhead + (Session.FrameSendRate - 1)) /
- Session.FrameSendRate) * Session.FrameSendRate;
- }
- else {
- packet.Frame = Frame + Session.MaxAhead;
- }
- packet.ID = PlayerPtr->ID;
- packet.Data.FrameInfo.CRC = ScenarioCRC;
- packet.Data.FrameInfo.CommandCount = cmd_count;
- packet.Data.FrameInfo.Delay = Session.MaxAhead;
- //------------------------------------------------------------------------
- // Send the event. For modem, this just sends to the other player;
- // for network, it sends to everyone we're connected to.
- //------------------------------------------------------------------------
- net->Send_Private_Message (&packet, (offsetof(EventClass, Data) +
- size_of(EventClass, Data.FrameInfo)), 0 );
- return;
- } // end of Send_FrameSync
- /***************************************************************************
- * Process_Receive_Packet -- processes an incoming packet *
- * *
- * This routine receives a packet from another system, adds it to our *
- * execution queue (the DoList), and updates my arrays of their frame #, *
- * their commands-sent, and their commands-received. *
- * *
- * INPUT: *
- * net ptr to connection manager *
- * multi_packet_buf buffer containing packet(s) to parse *
- * id id of sender *
- * their_frame array containing frame #'s of other players *
- * their_sent array containing command count of other players *
- * their_recv array containing # recv'd cmds from other players *
- * *
- * OUTPUT: *
- * RC_NORMAL: nothing unusual happened, although *
- * their_sent or their_recv may have been *
- * altered *
- * RC_PLAYER_READY: player has been heard from for the 1st time; *
- * this presumes that his original *
- * 'their_frame[]' value was -1 when this *
- * routine was called *
- * RC_SCENARIO_MISMATCH: FRAMEINFO scenario CRC doesn't match; *
- * normally only applies after loading a new *
- * scenario or save-game *
- * RC_DOLIST_FULL: fatal error; unable to add events to DoList *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static RetcodeType Process_Receive_Packet(ConnManClass *net,
- char *multi_packet_buf, int id, int packetlen, long *their_frame,
- unsigned short *their_sent, unsigned short *their_recv)
- {
- EventClass *event;
- int index;
- RetcodeType retcode = RC_NORMAL;
- int i;
- //------------------------------------------------------------------------
- // Get an event ptr to the incoming message
- //------------------------------------------------------------------------
- event = (EventClass *)multi_packet_buf;
- //------------------------------------------------------------------------
- // Get the index of the sender
- //------------------------------------------------------------------------
- index = net->Connection_Index(id);
- //------------------------------------------------------------------------
- // Compute the other player's frame # (at the time this packet was sent)
- //------------------------------------------------------------------------
- if (their_frame[index] <
- (int)(event->Frame - event->Data.FrameInfo.Delay)) {
- //.....................................................................
- // If the original frame # for this player is -1, it means we've heard
- // from this player for the 1st time; return the appropriate value.
- //.....................................................................
- if (their_frame[index]==-1) {
- retcode = RC_PLAYER_READY;
- }
- their_frame[index] = event->Frame - event->Data.FrameInfo.Delay;
- }
- //------------------------------------------------------------------------
- // Extract the other player's CommandCount. This count will include
- // the commands in this packet, if there are any.
- //------------------------------------------------------------------------
- if (event->Data.FrameInfo.CommandCount > their_sent[index]) {
- if ( abs(their_sent[index] - event->Data.FrameInfo.CommandCount) > 500) {
- FILE *fp;
- fp = fopen("badcount.txt","wt");
- if (fp) {
- fprintf(fp,"Event Type:%s\n",EventClass::EventNames[event->Type]);
- fprintf(fp,"Frame:%d ID:%d IsExec:%d\n",
- event->Frame,
- event->ID,
- event->IsExecuted);
- if (event->Type != EventClass::FRAMEINFO) {
- fprintf(fp,"Wrong Event Type!\n");
- } else {
- fprintf(fp,"CRC:%x CommandCount:%d Delay:%d\n",
- event->Data.FrameInfo.CRC,
- event->Data.FrameInfo.CommandCount,
- event->Data.FrameInfo.Delay);
- }
- }
- }
- their_sent[index] = event->Data.FrameInfo.CommandCount;
- }
- if (Debug_Print_Events) {
- if (event->Type == EventClass::FRAMESYNC) {
- printf("(%d) Received FRAMESYNC: ", Frame);
- } else {
- printf("(%d) Received FRAMEINFO: ", Frame);
- }
- printf("EvFrame:%d ID:%d CRC:%x CmdCount:%d Delay:%d\n",
- event->Frame,
- event->ID,
- event->Data.FrameInfo.CRC,
- event->Data.FrameInfo.CommandCount,
- event->Data.FrameInfo.Delay);
- }
- //------------------------------------------------------------------------
- // If this packet was not a FRAMESYNC packet:
- // - Add the events in it to our DoList
- // - Increment our commands-received counter by the number of non-
- // FRAMEINFO packets received
- //------------------------------------------------------------------------
- if (event->Type != EventClass::FRAMESYNC) {
- //.....................................................................
- // Break up the packet into its component events. A returned packet
- // count of -1 indicates a fatal queue-full error.
- //.....................................................................
- i = Breakup_Receive_Packet( multi_packet_buf, packetlen);
- if (i==-1) {
- return (RC_DOLIST_FULL);
- }
- //.....................................................................
- // Compute the actual # commands in the packet by subtracting off the
- // FRAMEINFO event
- //.....................................................................
- if ( (event->Type==EventClass::FRAMEINFO) && (i > 0)) {
- i--;
- }
- their_recv[index] += i;
- }
- //------------------------------------------------------------------------
- // If the event was a FRAMESYNC packet, there will be no commands to add,
- // but we must check the ScenarioCRC value.
- //------------------------------------------------------------------------
- else if (event->Data.FrameInfo.CRC != ScenarioCRC) {
- return (RC_SCENARIO_MISMATCH);
- }
- return (retcode);
- } // end of Process_Receive_Packet
- /***************************************************************************
- * Process_Serial_Packet -- Handles an incoming serial packet *
- * *
- * This routine is needed because the modem classes don't support a *
- * "global channel" like the network classes do, but that functionality is *
- * still needed for modem communications. Specifically, the modem dialogs *
- * transmit their own special packets back & forth, and messages are sent *
- * using a special packet type. Thus, we have to call this routine when *
- * we receive a modem packet, to allow it to process messages & dialog *
- * packets. *
- * *
- * INPUT: *
- * multi_packet_buf packet buffer to process *
- * first_time 1 = this is the 1st game frame *
- * *
- * OUTPUT: *
- * RC_NORMAL: this wasn't a SERIAL-type packet *
- * RC_SERIAL_PROCESSED: this was a SERIAL-type packet, and was *
- * processed; the other player is still connected, *
- * even if he's not in the game. *
- * RC_PLAYER_LEFT: other player has left the game *
- * RC_HUNG_UP: we're getting our own packets back; thus, the *
- * modem is mirroring our packets, which means the *
- * modem hung up! *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static RetcodeType Process_Serial_Packet(char *multi_packet_buf,
- int first_time)
- {
- SerialPacketType *serial_packet; // for parsing serial packets
- int player_gone;
- EventClass *event;
- //------------------------------------------------------------------------
- // Determine if this packet means that the other player has left the game
- //------------------------------------------------------------------------
- serial_packet = (SerialPacketType *)multi_packet_buf;
- player_gone = 0;
- //........................................................................
- // On Frame 0, only a SIGN_OFF means the other player left; the other
- // packet types may be left over from a previous session.
- //........................................................................
- if (first_time) {
- if (serial_packet->Command == SERIAL_SIGN_OFF) {
- player_gone = 1;
- }
- }
- //........................................................................
- // On subsequent frames, any of SIGN_OFF, TIMING, or SCORE_SCREEN means
- // the other player is gone.
- //........................................................................
- else {
- if (serial_packet->Command == SERIAL_SIGN_OFF ||
- serial_packet->Command == SERIAL_TIMING ||
- serial_packet->Command == SERIAL_SCORE_SCREEN ) {
- player_gone = 1;
- }
- }
- if (player_gone) {
- Destroy_Null_Connection(serial_packet->ScenarioInfo.Color, 0);
- return (RC_PLAYER_LEFT);
- }
- //------------------------------------------------------------------------
- // Process an incoming message
- //------------------------------------------------------------------------
- if (serial_packet->Command == SERIAL_MESSAGE) {
- if (!Session.Messages.Concat_Message(serial_packet->Name,
- serial_packet->ID, serial_packet->Message.Message, Rule.MessageDelay * TICKS_PER_MINUTE)) {
- #ifdef FIXIT_CSII // checked - ajw 9/28/98 - Appears to do nothing
- char *ptr = &serial_packet->Message.Message[0];
- if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) {
- Enable_Secret_Units();
- }
- #endif
- Session.Messages.Add_Message (serial_packet->Name,
- serial_packet->ID, serial_packet->Message.Message,
- (PlayerColorType)serial_packet->ID,
- TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
- Sound_Effect(VOC_INCOMING_MESSAGE);
- }
- //.....................................................................
- // Save this message in our last-message buffer
- //.....................................................................
- if (strlen (serial_packet->Message.Message)) {
- strcpy (Session.LastMessage, serial_packet->Message.Message);
- }
- //.....................................................................
- // Tell the map to do a partial update (just to force the
- // messages to redraw).
- //.....................................................................
- //Map.Flag_To_Redraw(false);
- Map.Flag_To_Redraw(true);
- return (RC_SERIAL_PROCESSED);
- }
- //------------------------------------------------------------------------
- // Any other SERIAL-type packet means the other player is still there;
- // throw them away, but let the caller know the connection is OK.
- //------------------------------------------------------------------------
- if ( (serial_packet->Command >= SERIAL_CONNECT &&
- serial_packet->Command < SERIAL_LAST_COMMAND) ||
- (serial_packet->Command >= SERIAL_REQ_SCENARIO &&
- serial_packet->Command <= SERIAL_NO_SCENARIO) ||
- Session.NumPlayers == 1) {
- return (RC_SERIAL_PROCESSED);
- }
- //........................................................................
- // are we getting our own packets back??
- //........................................................................
- event = (EventClass *)multi_packet_buf;
- if (event->Type <= EventClass::EMPTY || event->Type >= EventClass::LAST_EVENT) return (RC_SERIAL_PROCESSED);
- if (event->ID == PlayerPtr->ID) {
- return (RC_HUNG_UP);
- }
- return (RC_NORMAL);
- } // end of Process_Serial_Packet
- /***************************************************************************
- * Can_Advance -- determines if it's OK to advance to the next frame *
- * *
- * This routine uses the current values stored in their_frame[], *
- * their_send[], and their_recv[] to see if it's OK to advance to the next *
- * game frame. We must not advance if: *
- * - If our frame # would be too far ahead of the slowest player (the *
- * lowest their_frame[] value). "Too far" means *
- * (Frame >= their_frame + MaxAhead). *
- * - our current command count doesn't match the sent command count of one *
- * other player (meaning that we've missed a command packet from that *
- * player, and thus the frame # we're receiving from him may be due to a *
- * FRAMEINFO packet sent later than the command, so we shouldn't use *
- * this frame # to see if we should advance; we should wait until we *
- * have all the commands before we advance. *
- * *
- * Of course, this routine assumes the values in their_frame[] etc are *
- * kept current by the caller. *
- * *
- * INPUT: *
- * net ptr to connection manager *
- * max_ahead max frames ahead *
- * their_frame array of their frame #'s *
- * their_sent array of their sent command count *
- * their_recv array of their # received commands *
- * *
- * OUTPUT: *
- * 1 = OK to advance; 0 = not OK *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame,
- unsigned short *their_sent, unsigned short *their_recv)
- {
- long their_oldest_frame; // other players' oldest frame #
- int count_ok; // true = my cmd count matches theirs
- int i;
- //------------------------------------------------------------------------
- // Special case for modem: if the other player has left, go ahead and
- // advance to the next frame; don't wait on him.
- //------------------------------------------------------------------------
- if (Session.NumPlayers == 1) {
- return (1);
- }
- //------------------------------------------------------------------------
- // Find the oldest frame # in 'their_frame'
- //------------------------------------------------------------------------
- their_oldest_frame = Frame + 1000;
- for (i = 0; i < net->Num_Connections(); i++) {
- if (their_frame[i] < their_oldest_frame)
- their_oldest_frame = their_frame[i];
- }
- //------------------------------------------------------------------------
- // I can advance to the next frame IF:
- // 1) I'm less than a one-way propagation delay ahead of the other
- // players' frame numbers, AND
- // 2) their_recv[i] >= their_sent[i] (ie I've received all the commands
- // the other players have sent so far).
- //------------------------------------------------------------------------
- count_ok = 1;
- for (i = 0; i < net->Num_Connections(); i++) {
- if (their_recv[i] < their_sent[i]) {
- count_ok = 0;
- break;
- }
- }
- if (count_ok && (Frame < (their_oldest_frame + max_ahead))) {
- return (1);
- }
- return (0);
- } // end of Can_Advance
- /***************************************************************************
- * Process_Reconnect_Dialog -- processes the reconnection dialog *
- * *
- * This routine [re]draws the reconnection dialog; if 'reconn' is set, *
- * it tells the user who we're trying to reconnect to; otherwise, is just *
- * says something generic like "Waiting for connections". *
- * *
- * INPUT: *
- * timeout_timer ptr to count down timer, showing time remaining *
- * their_frame array of other players' frame #'s *
- * num_conn # connections in 'their_frame' *
- * reconn 1 = reconnect, 0 = waiting for first-time connection *
- * fresh 1 = draw from scratch, 0 = only update time counter *
- * *
- * OUTPUT: *
- * 1 = user wants to cancel, 0 = not *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static int Process_Reconnect_Dialog(CDTimerClass<SystemTimerClass> *timeout_timer,
- long *their_frame, int num_conn, int reconn, int fresh)
- {
- static int displayed_time = 0; // time value currently displayed
- int new_time;
- int oldest_index; // index of person requiring a reconnect
- int i,j;
- //------------------------------------------------------------------------
- // Convert the timer to seconds
- //------------------------------------------------------------------------
- new_time = *timeout_timer / 60;
- //------------------------------------------------------------------------
- // If the timer has changed, or 'fresh' is set, redraw the dialog
- //------------------------------------------------------------------------
- if (fresh || (new_time != displayed_time)) {
- //.....................................................................
- // Find the index of the person we're trying to reconnect to
- //.....................................................................
- if (reconn) {
- j = 0x7fffffff;
- oldest_index = 0;
- for (i = 0; i < num_conn; i++) {
- if (their_frame[i] < j) {
- j = their_frame[i];
- oldest_index = i;
- }
- }
- }
- Net_Reconnect_Dialog(reconn, fresh, oldest_index, new_time);
- }
- displayed_time = new_time;
- //........................................................................
- // If user hits ESC, bail out
- //........................................................................
- if (Keyboard->Check()) {
- if (Keyboard->Get() == KN_ESC) {
- return (1);
- }
- }
- return (0);
- } // end of Process_Reconnect_Dialog
- /***************************************************************************
- * Handle_Timeout -- handles a timeout in the wait-for-players loop *
- * *
- * This routine "gracefully" handles a timeout in the frame-sync loop. *
- * The timeout must be handled differently by a modem game or network *
- * game. *
- * *
- * The modem game must detect if the other player is still connected *
- * physically, even if he's not playing the game any more; if so, this *
- * routine returns an OK status. If the other player isn't even *
- * physically connected, an error is returned. *
- * *
- * The network game must find the connection that's causing the timeout, *
- * and destroy it. The game continues, even if there are no more human *
- * players left. *
- * *
- * INPUT: *
- * net ptr to connection manager *
- * their_frame array containing frame #'s of other players *
- * their_sent array containing command count of other players *
- * their_recv array containing # recv'd cmds from other players *
- * *
- * OUTPUT: *
- * 1 = it's OK; reset timeout timers & keep processing *
- * 0 = game over, man *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static int Handle_Timeout(ConnManClass *net, long *their_frame,
- unsigned short *their_sent, unsigned short *their_recv)
- {
- int oldest_index; // index of person requiring a reconnect
- int i,j;
- int id;
- //------------------------------------------------------------------------
- // For modem, attempt to reconnect; if that fails, save the game & bail.
- //------------------------------------------------------------------------
- if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
- if ( net->Num_Connections() ) {
- if (!Reconnect_Modem()) {
- #ifndef FIXIT_MULTI_SAVE
- //...............................................................
- // Set 'Session.EmergencySave', so when this game is loaded, we
- // won't check the CRC of the game state (this system & the
- // other may be on different frames, in which case the CRC
- // won't match).
- //...............................................................
- Session.EmergencySave = 1;
- //Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME));
- Session.EmergencySave = 0;
- #endif // FIXIT_MULTI_SAVE
- return (0);
- } else {
- return (1);
- }
- }
- }
- //------------------------------------------------------------------------
- // For network, destroy the oldest connection
- //------------------------------------------------------------------------
- else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET ||
- Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
- j = 0x7fffffff;
- oldest_index = 0;
- for (i = 0; i < net->Num_Connections(); i++) {
- if (their_frame[i] < j) {
- j = their_frame[i];
- oldest_index = i;
- }
- }
- id = net->Connection_ID(oldest_index);
- #ifdef WIN32
- /*
- ** Send the game statistics packet now if the game is effectivly over
- */
- if (Session.Players.Count() == 2 &&
- Session.Type == GAME_INTERNET &&
- !GameStatisticsPacketSent) {
- Register_Game_End_Time();
- ConnectionLost = true;
- Send_Statistics_Packet(); // Disconnect, and I'll be the only one left.
- }
- #endif //WIN32
- if (id != ConnManClass::CONNECTION_NONE) {
- for (i = oldest_index; i < net->Num_Connections() - 1; i++) {
- their_frame[i] = their_frame[i+1];
- their_sent[i] = their_sent[i+1];
- their_recv[i] = their_recv[i+1];
- }
- if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
- Destroy_Connection(id,1);
- }
- #if(TEN)
- else if (Session.Type == GAME_TEN) {
- Destroy_TEN_Connection(id,1);
- }
- #endif
- #if(MPATH)
- else if (Session.Type == GAME_MPATH) {
- Destroy_MPATH_Connection(id,1);
- }
- #endif
- }
- }
- return (1);
- } // end of Handle_Timeout
- /***************************************************************************
- * Stop_Game -- stops the game *
- * *
- * This routine clears any global flags that need it, in preparation for *
- * halting the game prematurely. *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/22/1995 BRR : Created. *
- *=========================================================================*/
- static void Stop_Game(void)
- {
- Session.LoadGame = false;
- Session.EmergencySave = false;
- GameActive = 0;
- if (IsMono) {
- MonoClass::Disable();
- }
- #ifdef WIN32
- if (Session.Type == GAME_INTERNET){
- ConnectionLost = true;
- Send_Statistics_Packet(); // Stop_Game()
- }
- #endif //WIN32
- return;
- } // end of Stop_Game
- /***************************************************************************
- * Build_Send_Packet -- Builds a big packet from a bunch of little ones. *
- * *
- * This routine takes events from the OutList, and puts them into a *
- * "meta-packet", which is transmitted to all systems we're connected to. *
- * Also, these events are added to our own DoList. *
- * *
- * Every Meta-Packet we send uses a FRAMEINFO packet as a header; this *
- * tells the other systems what frame we're on, as well as serving as a *
- * standard packet header. *
- * *
- * INPUT: *
- * buf buffer to store packet in *
- * bufsize max size of buffer *
- * frame_delay desired frame delay to attach to all outgoing packets *
- * num_cmds value to use for the CommandCount field *
- * cap max # events to send *
- * *
- * OUTPUT: *
- * new size of packet *
- * *
- * WARNINGS: *
- * 'num_cmds' should be the total of of commands, including all those sent *
- * this frame! *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static int Build_Send_Packet(void *buf, int bufsize, int frame_delay,
- int num_cmds, int cap)
- {
- int size = 0;
- EventClass *finfo;
- //------------------------------------------------------------------------
- // All events start with a FRAMEINFO event; fill this part in.
- //------------------------------------------------------------------------
- //........................................................................
- // Set the event type
- //........................................................................
- finfo = (EventClass *)buf;
- finfo->Type = EventClass::FRAMEINFO;
- //........................................................................
- // Set the frame to execute this event on; this is protocol-specific
- //........................................................................
- if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
- finfo->Frame = ((Frame + frame_delay + (Session.FrameSendRate - 1)) /
- Session.FrameSendRate) * Session.FrameSendRate;
- }
- else {
- finfo->Frame = Frame + frame_delay;
- }
- //........................................................................
- // Fill in the rest of the event
- //........................................................................
- finfo->ID = PlayerPtr->ID;
- finfo->Data.FrameInfo.CRC = GameCRC;
- finfo->Data.FrameInfo.CommandCount = num_cmds;
- finfo->Data.FrameInfo.Delay = frame_delay;
- //------------------------------------------------------------------------
- // Initialize the # of bytes processed; this is protocol-specific
- //------------------------------------------------------------------------
- if (Session.CommProtocol==COMM_PROTOCOL_SINGLE_NO_COMP) {
- size += sizeof(EventClass);
- }
- else {
- size += (offsetof(EventClass, Data) +
- size_of(EventClass, Data.FrameInfo));
- }
- //------------------------------------------------------------------------
- // Transfer all events from the OutList into the DoList, building our
- // packet while we go.
- //------------------------------------------------------------------------
- switch (Session.CommProtocol) {
- //.....................................................................
- // COMM_PROTOCOL_SINGLE_NO_COMP:
- // We'll send at least a FRAMEINFO every single frame, no compression
- //.....................................................................
- case (COMM_PROTOCOL_SINGLE_NO_COMP):
- size = Add_Uncompressed_Events(buf, bufsize, frame_delay, size, cap);
- break;
- //.....................................................................
- // COMM_PROTOCOL_SINGLE_E_COMP:
- // Compress a group of packets into our send buffer; send out
- // compressed packets every frame.
- // COMM_PROTOCOL_MULTI_E_COMP:
- // Compress a group of packets into our send buffer; send out
- // compressed packets every 'n' frames.
- //.....................................................................
- case (COMM_PROTOCOL_SINGLE_E_COMP):
- case (COMM_PROTOCOL_MULTI_E_COMP):
- size = Add_Compressed_Events(buf, bufsize, frame_delay, size, cap);
- break;
- //.....................................................................
- // Default: We have no idea what to do, so do nothing.
- //.....................................................................
- default:
- size = 0;
- break;
- }
- return( size );
- } /* end of Build_Send_Packet */
- /***************************************************************************
- * Add_Uncompressed_Events -- adds uncompressed events to a packet *
- * *
- * INPUT: *
- * buf buffer to store packet in *
- * bufsize max size of buffer *
- * frame_delay desired frame delay to attach to all outgoing packets *
- * size current packet size *
- * cap max # events to process *
- * *
- * OUTPUT: *
- * new size value *
- * *
- * WARNINGS: *
- * This routine MUST check to be sure it doesn't overflow the buffer. *
- * *
- * HISTORY: *
- * 11/21/1995 DRD : Created. *
- *=========================================================================*/
- static int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay,
- int size, int cap)
- {
- int num = 0; // # of events processed
- int ev_size; // size of event we're adding
- //------------------------------------------------------------------------
- // Loop until there are no more events, or we've processed our max # of
- // events, or the buffer is full.
- //------------------------------------------------------------------------
- while (OutList.Count && (num < cap)) {
- Keyboard->Check();
- if (OutList.First().Type==EventClass::ADDPLAYER) {
- ev_size = sizeof(EventClass) + OutList.First().Data.Variable.Size;
- }
- else {
- ev_size = sizeof(EventClass);
- }
- //.....................................................................
- // Will the next event exceed the size of the buffer? If so, break.
- //.....................................................................
- if ( (size + ev_size) > bufsize ) {
- return (size);
- }
- //.....................................................................
- // Set the event's frame delay
- //.....................................................................
- OutList.First().Frame = Frame + frame_delay;
- //.....................................................................
- // Set the event's ID
- //.....................................................................
- OutList.First().ID = PlayerPtr->ID;
- //.....................................................................
- // Transfer the event in OutList to DoList, un-queue the OutList
- // event. If the DoList is full, stop transferring immediately.
- //.....................................................................
- OutList.First().IsExecuted = 0;
- if (!DoList.Add(OutList.First())) {
- return (size);
- }
- #ifdef MIRROR_QUEUE
- MirrorList.Add(OutList.First());
- #endif
- //.....................................................................
- // Add event to the send packet
- //.....................................................................
- if (OutList.First().Type==EventClass::ADDPLAYER) {
- memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) );
- size += sizeof(EventClass);
- memcpy ( ((char *)buf) + size,
- OutList.First().Data.Variable.Pointer,
- OutList.First().Data.Variable.Size);
- size += OutList.First().Data.Variable.Size;
- }
- else {
- memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) );
- size += sizeof(EventClass);
- }
- //.....................................................................
- // Increment our event counter; delete the last event from the queue
- //.....................................................................
- num++;
- OutList.Next();
- }
- return (size);
- } // end of Add_Uncompressed_Events
- /***************************************************************************
- * Add_Compressed_Events -- adds an compressed events to a packet *
- * *
- * INPUT: *
- * buf buffer to store packet in *
- * bufsize max size of buffer *
- * frame_delay desired frame delay to attach to all outgoing packets *
- * size reference to current packet size *
- * cap max # events to process *
- * *
- * OUTPUT: *
- * new size value *
- * *
- * WARNINGS: *
- * This routine MUST check to be sure it doesn't overflow the buffer. *
- * *
- * HISTORY: *
- * 11/21/1995 DRD : Created. *
- *=========================================================================*/
- static int Add_Compressed_Events(void *buf, int bufsize, int frame_delay,
- int size, int cap)
- {
- int num = 0; // # of events processed
- EventClass::EventType eventtype; // type of event being compressed
- EventClass prevevent; // last event processed
- int datasize; // size of element plucked from event union
- int storedsize; // actual # bytes stored from event
- unsigned char *unitsptr = NULL; // ptr to buffer pos to store mega. rep count
- unsigned char numunits = 0; // megamission rep count value
- bool missiondup = false; // flag: is this event a megamission repeat?
- //------------------------------------------------------------------------
- // clear previous event
- //------------------------------------------------------------------------
- memset (&prevevent, 0, sizeof(EventClass));
- if (Debug_Print_Events) {
- printf("\n(%d) Building Send Packet\n", Frame);
- }
- //------------------------------------------------------------------------
- // Loop until there are no more events, we've processed our max # of
- // events, or the buffer is full.
- //------------------------------------------------------------------------
- while (OutList.Count && (num < cap)) {
- Keyboard->Check();
- eventtype = OutList.First().Type;
- datasize = EventClass::EventLength[ eventtype ];
- //.....................................................................
- // For a variable-sized event, pull the size from the event; otherwise,
- // the size will be the data element size plus the event type value.
- // (The other data elements in the event, Frame, ID, etc, are stored
- // in the packet header.)
- //.....................................................................
- if (eventtype==EventClass::ADDPLAYER) {
- storedsize = datasize + sizeof (EventClass::EventType) +
- OutList.First().Data.Variable.Size;
- }
- else {
- storedsize = datasize + sizeof (EventClass::EventType);
- }
- //.....................................................................
- // MegaMission compression: MegaMissions are stored as:
- // EventType
- // Rep Count
- // MegaMission structure (event # 1 only)
- // Whom #2
- // Whom #3
- // Whom #4
- // ...
- // Whom #n
- //.....................................................................
- if (prevevent.Type == EventClass::MEGAMISSION) {
- //..................................................................
- // If previous & current events are both MegaMissions:
- //..................................................................
- if (eventtype == EventClass::MEGAMISSION) {
- //...............................................................
- // If the Mission, Target, & Destination are the same, compress
- // the events into one:
- // - Change datasize to the size of the 'Whom' field only
- // - set total # bytes to store to the size of the 'Whom' only
- // - increment the MegaMission rep count
- // - set the MegaMission rep flag
- //...............................................................
- if (OutList.First().Data.MegaMission.Mission ==
- prevevent.Data.MegaMission.Mission &&
- OutList.First().Data.MegaMission.Target ==
- prevevent.Data.MegaMission.Target &&
- OutList.First().Data.MegaMission.Destination ==
- prevevent.Data.MegaMission.Destination) {
- if (Debug_Print_Events) {
- printf(" adding Whom:%x (%x) Mission:%s Target:%x (%x) Dest:%x (%x)\n",
- OutList.First().Data.MegaMission.Whom.As_TARGET(),
- OutList.First().Data.MegaMission.Whom,
- MissionClass::Mission_Name(OutList.First().Data.MegaMission.Mission),
- OutList.First().Data.MegaMission.Target.As_TARGET(),
- OutList.First().Data.MegaMission.Target,
- OutList.First().Data.MegaMission.Destination.As_TARGET(),
- OutList.First().Data.MegaMission.Destination);
- }
- datasize = sizeof(prevevent.Data.MegaMission.Whom);
- storedsize = datasize;
- numunits++;
- missiondup = true;
- }
- //...............................................................
- // Data doesn't match; start a new run of MegaMissions:
- // - Store previous MegaMission rep count
- // - Init 'unitsptr' to buffer pos after next EventType
- // - set total # bytes to store to 'datasize' + sizeof(EventType) +
- // sizeof (numunits)
- // - init the MegaMission rep count to 1
- // - clear the MegaMission rep flag
- //...............................................................
- else {
- if (Debug_Print_Events) {
- printf(" New MEGAMISSION run:\n");
- }
- *unitsptr = numunits;
- unitsptr = ((unsigned char *)buf) + size +
- sizeof(EventClass::EventType);
- storedsize += sizeof(numunits);
- numunits = 1;
- missiondup = false;
- }
- }
- //..................................................................
- // Previous event was a MegaMission, but this one isn't: end the
- // run of MegaMissions:
- // - Store previous MegaMission rep count
- // - Clear variables
- //..................................................................
- else {
- *unitsptr = numunits; // save # events in our run
- unitsptr = NULL; // init other values
- numunits = 0;
- missiondup = false;
- }
- }
- //.....................................................................
- // The previous event is not a MEGAMISSION but the current event is:
- // Set up a new run of MegaMissions:
- // - Init 'unitsptr' to buffer pos after next EventType
- // - set total # bytes to store to 'datasize' + sizeof(EventType) +
- // sizeof (numunits)
- // - init the MegaMission rep count to 1
- // - clear the MegaMission rep flag
- //.....................................................................
- else if (eventtype == EventClass::MEGAMISSION) {
- if (Debug_Print_Events) {
- printf(" New MEGAMISSION run:\n");
- }
- unitsptr = ((unsigned char *)buf) + size +
- sizeof(EventClass::EventType);
- storedsize += sizeof(numunits);
- numunits = 1;
- missiondup = false;
- }
- //.....................................................................
- // Will the next event exceed the size of the buffer? If so,
- // stop compressing.
- //.....................................................................
- if ( (size + storedsize) > bufsize )
- break;
- //.....................................................................
- // Set the event's frame delay (this is protocol-dependent)
- //.....................................................................
- if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
- OutList.First().Frame = ((Frame + frame_delay +
- (Session.FrameSendRate - 1)) / Session.FrameSendRate) *
- Session.FrameSendRate;
- }
- else {
- OutList.First().Frame = Frame + frame_delay;
- }
- //.....................................................................
- // Set the event's ID
- //.....................................................................
- OutList.First().ID = PlayerPtr->ID;
- //.....................................................................
- // Transfer the event in OutList to DoList, un-queue the OutList event.
- // If the DoList is full, stop transferring immediately.
- //.....................................................................
- OutList.First().IsExecuted = 0;
- if ( !DoList.Add( OutList.First() ) ) {
- break;
- }
- #ifdef MIRROR_QUEUE
- MirrorList.Add(OutList.First());
- #endif
- //---------------------------------------------------------------------
- // Compress the event into the send packet buffer
- //---------------------------------------------------------------------
- switch ( eventtype ) {
- //..................................................................
- // RESPONSE_TIME: just use the Delay field of the FrameInfo union
- //..................................................................
- case (EventClass::RESPONSE_TIME):
- *(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
- memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType),
- &OutList.First().Data.FrameInfo.Delay, datasize );
- size += (datasize + sizeof(EventClass::EventType));
- break;
- //..................................................................
- // MEGAMISSION:
- //..................................................................
- case (EventClass::MEGAMISSION):
- //...............................................................
- // Repeated mission in a run:
- // - Update the rep count (in case we break out)
- // - Copy the Whom field only
- //...............................................................
- if (missiondup) {
- *unitsptr = numunits;
- memcpy ( ((char *)buf) + size,
- &OutList.First().Data.MegaMission.Whom, datasize );
- size += datasize;
- }
- //...............................................................
- // 1st mission in a run:
- // - Init the rep count (in case we break out)
- // - Set the EventType
- // - Copy the MegaMission structure, leaving room for 'numunits'
- //...............................................................
- else {
- *unitsptr = numunits;
- *(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
- memcpy ( ((char *)buf) + size +
- sizeof(EventClass::EventType) + sizeof(numunits),
- &OutList.First().Data.MegaMission, datasize );
- size += (datasize + sizeof(EventClass::EventType) + sizeof(numunits));
- }
- break;
- //..................................................................
- // Variable-sized packets: Copy the packet Size & the buffer
- //..................................................................
- case (EventClass::ADDPLAYER):
- *(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
- memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType),
- &OutList.First().Data.Variable.Size, datasize );
- size += (datasize + sizeof(EventClass::EventType));
- memcpy ( ((char *)buf) + size,
- OutList.First().Data.Variable.Pointer,
- OutList.First().Data.Variable.Size);
- size += OutList.First().Data.Variable.Size;
- break;
- //..................................................................
- // Default case: Just copy over the data field from the union
- //..................................................................
- default:
- *(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
- memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType),
- &OutList.First().Data, datasize );
- size += (datasize + sizeof(EventClass::EventType));
- break;
- }
- //---------------------------------------------------------------------
- // update # events processed
- //---------------------------------------------------------------------
- num++;
- //---------------------------------------------------------------------
- // Update 'prevevent'
- //---------------------------------------------------------------------
- memcpy ( &prevevent, &OutList.First(), sizeof(EventClass) );
- //---------------------------------------------------------------------
- // Go to the next event to process
- //---------------------------------------------------------------------
- OutList.Next();
- }
- if (Debug_Print_Events) {
- printf("\n");
- }
- return (size);
- } // end of Add_Compressed_Events
- /***************************************************************************
- * Breakup_Receive_Packet -- Splits a big packet into little ones. *
- * *
- * INPUT: *
- * buf buffer to break up *
- * bufsize length of buffer *
- * *
- * OUTPUT: *
- * # events added to queue, -1 if fatal error (queue is full) *
- * (return value includes any FRAMEINFO packets encountered; *
- * FRAMESYNC's are ignored) *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static int Breakup_Receive_Packet(void *buf, int bufsize )
- {
- int count = 0;
- /*
- ** is there enough leftover for another record
- */
- switch (Session.CommProtocol) {
- case (COMM_PROTOCOL_SINGLE_NO_COMP):
- count = Extract_Uncompressed_Events(buf, bufsize);
- break;
- default:
- count = Extract_Compressed_Events(buf, bufsize);
- break;
- }
- return (count);
- } /* end of Breakup_Receive_Packet */
- /***************************************************************************
- * Extract_Uncompressed_Events -- extracts events from a packet *
- * *
- * INPUT: *
- * buf buffer containing events to extract *
- * bufsize length of 'buf' *
- * *
- * OUTPUT: *
- * # events extracted *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 DRD : Created. *
- *=========================================================================*/
- static int Extract_Uncompressed_Events(void *buf, int bufsize)
- {
- int count = 0;
- int pos = 0;
- int leftover = bufsize;
- EventClass *event;
- //------------------------------------------------------------------------
- // Loop until there are no more events in the packet
- //------------------------------------------------------------------------
- while (leftover >= sizeof(EventClass) ) {
- Keyboard->Check();
- event = (EventClass *)(((char *)buf) + pos);
- //.....................................................................
- // add event to the DoList, only if it's not a FRAMESYNC
- // (but FRAMEINFO's do get added.)
- //.....................................................................
- if (event->Type != EventClass::FRAMESYNC) {
- event->IsExecuted = 0;
- //..................................................................
- // Special processing for variable-sized events
- //..................................................................
- if (event->Type == EventClass::ADDPLAYER) {
- event->Data.Variable.Pointer = new char[event->Data.Variable.Size];
- memcpy (event->Data.Variable.Pointer,
- ((char *)buf) + sizeof(EventClass),
- event->Data.Variable.Size);
- pos += event->Data.Variable.Size;
- leftover -= event->Data.Variable.Size;
- }
- if (!DoList.Add( *event )) {
- if (event->Type == EventClass::ADDPLAYER) {
- delete [] event->Data.Variable.Pointer;
- }
- return (-1);
- }
- #ifdef MIRROR_QUEUE
- MirrorList.Add(*event);
- #endif
- //..................................................................
- // Keep count of how many events we add to the queue
- //..................................................................
- count++;
- }
- //.....................................................................
- // Point to the next position in the buffer; decrement our 'leftover'
- //.....................................................................
- pos += sizeof(EventClass);
- leftover -= sizeof(EventClass);
- }
- return (count);
- } // end of Extract_Uncompressed_Events
- /***************************************************************************
- * Extract_Compressed_Events -- extracts events from a packet *
- * *
- * INPUT: *
- * buf buffer containing events to extract *
- * bufsize length of 'buf' *
- * *
- * OUTPUT: *
- * # events extracted *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 DRD : Created. *
- *=========================================================================*/
- static int Extract_Compressed_Events(void *buf, int bufsize)
- {
- int pos = 0; // current buffer parsing position
- int leftover = bufsize; // # bytes left to process
- EventClass *event; // event ptr for parsing buffer
- int count = 0; // # events processed
- int datasize = 0; // size of data to copy
- EventClass eventdata; // stores Frame, ID, etc
- unsigned char numunits = 0; // # units stored in compressed MegaMissions
- //------------------------------------------------------------------------
- // Clear work event structure
- //------------------------------------------------------------------------
- memset (&eventdata, 0, sizeof(EventClass));
- //------------------------------------------------------------------------
- // Assume the first event is a FRAMEINFO event
- // Init 'datasize' to the amount of data to copy, minus the EventType value
- // For the 1st packet only, this will include all info before the Data
- // union, plus the size of the FrameInfo structure, minus the EventType size.
- //------------------------------------------------------------------------
- datasize = (offsetof(EventClass, Data) +
- size_of(EventClass, Data.FrameInfo)) - sizeof(EventClass::EventType);
- event = (EventClass *)(((char *)buf) + pos);
- while (leftover >= (datasize + sizeof(EventClass::EventType)) ) {
- Keyboard->Check();
- //.....................................................................
- // add event to the DoList, only if it's not a FRAMESYNC
- // (but FRAMEINFO's do get added.)
- //.....................................................................
- if (event->Type != EventClass::FRAMESYNC) {
- //..................................................................
- // initialize the common data from the FRAMEINFO event
- // keeping IsExecuted 0
- //..................................................................
- if (event->Type == EventClass::FRAMEINFO) {
- eventdata.Frame = event->Frame;
- eventdata.ID = event->ID;
- //...............................................................
- // Adjust position past the common data
- //...............................................................
- pos += (offsetof(EventClass, Data) -
- sizeof(EventClass::EventType));
- leftover -= (offsetof(EventClass, Data) -
- sizeof(EventClass::EventType));
- }
- //..................................................................
- // if MEGAMISSION event get the number of units (events to generate)
- //..................................................................
- else if (event->Type == EventClass::MEGAMISSION) {
- numunits = *(((unsigned char *)buf) + pos + sizeof(eventdata.Type));
- pos += sizeof(numunits);
- leftover -= sizeof(numunits);
- }
- //..................................................................
- // clear the union data portion of the event
- //..................................................................
- memset (&eventdata.Data, 0, sizeof(eventdata.Data));
- eventdata.Type = event->Type;
- datasize = EventClass::EventLength[ eventdata.Type ];
- switch (eventdata.Type) {
- case (EventClass::RESPONSE_TIME):
- memcpy ( &eventdata.Data.FrameInfo.Delay,
- ((char *)buf) + pos + sizeof(EventClass::EventType),
- datasize );
- break;
- case (EventClass::ADDPLAYER):
- memcpy ( &eventdata.Data.Variable.Size,
- ((char *)buf) + pos + sizeof(EventClass::EventType),
- datasize );
- eventdata.Data.Variable.Pointer =
- new char[eventdata.Data.Variable.Size];
- memcpy (eventdata.Data.Variable.Pointer,
- ((char *)buf) + pos + sizeof(EventClass::EventType) + datasize,
- eventdata.Data.Variable.Size);
- pos += eventdata.Data.Variable.Size;
- leftover -= eventdata.Data.Variable.Size;
- break;
- case (EventClass::MEGAMISSION):
- memcpy ( &eventdata.Data.MegaMission,
- ((char *)buf) + pos + sizeof(EventClass::EventType),
- datasize );
- if (numunits > 1) {
- pos += (datasize + sizeof(EventClass::EventType));
- leftover -= (datasize + sizeof(EventClass::EventType));
- datasize = sizeof(eventdata.Data.MegaMission.Whom);
- while (numunits) {
- Keyboard->Check();
- if ( !DoList.Add( eventdata ) ) {
- return (-1);
- }
- #ifdef MIRROR_QUEUE
- MirrorList.Add( eventdata );
- #endif
- //......................................................
- // Keep count of how many events we add to the queue
- //......................................................
- count++;
- numunits--;
- memcpy ( &eventdata.Data.MegaMission.Whom,
- ((char *)buf) + pos, datasize );
- //......................................................
- // if one unit left fall thru to normal code
- //......................................................
- if (numunits == 1) {
- datasize -= sizeof(EventClass::EventType);
- break;
- }
- else {
- pos += datasize;
- leftover -= datasize;
- }
- }
- }
- break;
- default:
- memcpy ( &eventdata.Data,
- ((char *)buf) + pos + sizeof(EventClass::EventType),
- datasize );
- break;
- }
- if ( !DoList.Add( eventdata ) ) {
- if (eventdata.Type == EventClass::ADDPLAYER) {
- delete [] eventdata.Data.Variable.Pointer;
- }
- return (-1);
- }
- #ifdef MIRROR_QUEUE
- MirrorList.Add( eventdata );
- #endif
- //..................................................................
- // Keep count of how many events we add to the queue
- //..................................................................
- count++;
- pos += (datasize + sizeof(EventClass::EventType));
- leftover -= (datasize + sizeof(EventClass::EventType));
- if (leftover) {
- event = (EventClass *)(((char *)buf) + pos);
- datasize = EventClass::EventLength[ event->Type ];
- if (event->Type == EventClass::MEGAMISSION) {
- datasize += sizeof(numunits);
- }
- }
- }
- //.....................................................................
- // FRAMESYNC event: This >should< be the only event in the buffer,
- // and it will be uncompressed.
- //.....................................................................
- else {
- pos += (datasize + sizeof(EventClass::EventType));
- leftover -= (datasize + sizeof(EventClass::EventType));
- event = (EventClass *)(((char *)buf) + pos);
- //..................................................................
- // size of FRAMESYNC event - EventType size
- //..................................................................
- datasize = (offsetof(EventClass, Data) +
- size_of(EventClass, Data.FrameInfo)) -
- sizeof(EventClass::EventType);
- }
- }
- return (count);
- } // end of Extract_Compressed_Events
- /***************************************************************************
- * Execute_DoList -- Executes commands from the DoList *
- * *
- * This routine executes any events in the DoList that need to be executed *
- * on the current game frame. The events must be executed in a special *
- * order, so that all systems execute all events in exactly the same *
- * order. *
- * *
- * This routine also handles checking the Game CRC sent by other systems *
- * against my own, to be sure we're still in sync. *
- * *
- * INPUT: *
- * max_houses # houses to execute commands for *
- * base_house HousesType to start with *
- * net ptr to connection manager; NULL if none *
- * skip_crc a frame-based countdown timer; if it's non-zero, the *
- * CRC check will be skipped. Ignored if NULL. *
- * their_frame array of their frame #'s *
- * their_sent array of # commands they've sent *
- * their_recv array of # commands I've received from them *
- * *
- * (their_xxx are ignored if 'net' is NULL.) *
- * *
- * OUTPUT: *
- * 1 = OK, 0 = some error occurred (CRC error, packet rcv'd too late.) *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static int Execute_DoList(int max_houses, HousesType base_house,
- ConnManClass *net, CDTimerClass<FrameTimerClass> *skip_crc,
- long *their_frame, unsigned short *their_sent, unsigned short *their_recv)
- {
- HousesType house;
- HouseClass *hptr;
- int i,j,k;
- int index;
- int check_crc;
- Check_Mirror();
- #if(TIMING_FIX)
- //
- // If MPlayerMaxAhead is recomputed such that it increases, the systems
- // may try to free-run to the new MaxAhead value. If so, they may miss
- // an event that was generated after the TIMING event was created, but
- // before it executed; this event will be scheduled with the older,
- // shorter MaxAhead value. If a system doesn't receive this event, it
- // may execute past the frame it's scheduled to execute on, creating
- // a Packet-Recieved-Too-Late error. To prevent this, find any events
- // that are scheduled to execute during this "period of vulnerability",
- // and re-schedule for the end of that period.
- //
- for (j = 0; j < DoList.Count; j++) {
- if (DoList[j].Type != EventClass::FRAMEINFO &&
- DoList[j].Frame > NewMaxAheadFrame1 &&
- DoList[j].Frame < NewMaxAheadFrame2) {
- DoList[j].Frame = NewMaxAheadFrame2;
- #ifdef MIRROR_QUEUE
- MirrorList[j].Frame = NewMaxAheadFrame2;
- #endif
- }
- }
- #endif
- //------------------------------------------------------------------------
- // Execute the DoList. Events must be executed in the same order on all
- // systems; so, execute them in the order of the HouseClass array. This
- // array is stored in the same order on all systems.
- //------------------------------------------------------------------------
- for (i = 0; i < max_houses; i++) {
- //.....................................................................
- // Convert our index into a HousesType value
- //.....................................................................
- house = (HousesType)(i + base_house);
- hptr = HouseClass::As_Pointer(house);
- //.....................................................................
- // If for some reason this house doesn't exist, skip it.
- // Also, if this house has exited the game, skip it. (The user can
- // generate events after he exits, because the exit event is scheduled
- // at least FrameSendRate*3 frames ahead. If one system gets these
- // packets & another system doesn't, they'll go out of sync because
- // they aren't checking the CommandCount for that house, since that
- // house isn't connected any more.)
- //.....................................................................
- if (!hptr) {
- continue;
- }
- if (!hptr->IsHuman) {
- continue;
- }
- //.....................................................................
- // Loop through all events
- //.....................................................................
- for (j = 0; j < DoList.Count; j++) {
- if (net)
- Update_Queue_Mono (net, 6);
- //..................................................................
- // If this event was from the currently-executing player ID, and it's
- // time to execute it, execute it.
- //..................................................................
- if (DoList[j].ID == hptr->ID && Frame >= DoList[j].Frame &&
- !DoList[j].IsExecuted) {
- //...............................................................
- // Error if it's too late to execute this packet!
- // (Hack: disable this check for solo or skirmish mode.)
- //...............................................................
- if (Frame > DoList[j].Frame && DoList[j].Type !=
- EventClass::FRAMEINFO && Session.Type != GAME_NORMAL &&
- Session.Type != GAME_SKIRMISH) {
- #if(TEN)
- Send_TEN_Packet_Too_Late();
- #endif // TEN
- #if(MPATH)
- //Send_MPATH_Packet_Too_Late();
- #endif // MPATH
- Dump_Packet_Too_Late_Stuff(&DoList[j], net, their_frame,
- their_sent, their_recv);
- WWMessageBox().Process (TXT_PACKET_TOO_LATE);
- return (0);
- }
- //...............................................................
- // Only execute EXIT & OPTIONS commands if they're from myself.
- //...............................................................
- if (DoList[j].Type==EventClass::EXIT ||
- DoList[j].Type==EventClass::OPTIONS) {
- #ifdef WIN32
- if (DoList[j].Type==EventClass::EXIT) {
- /*
- ** Flag that this house lost because it quit.
- */
- HousesType quithouse;
- HouseClass *quithptr;
- for (int player = 0; player < max_houses ; player++) {
- quithouse = (HousesType)(player + base_house);
- quithptr = HouseClass::As_Pointer(quithouse);
- if (!quithptr) {
- continue;
- }
- if (quithptr->ID == DoList[j].ID) {
- quithptr->IsGiverUpper = true;
- break;
- }
- }
- /*
- ** Send the game statistics packet now since the game is effectivly over
- */
- if (Session.Players.Count() == 2 &&
- Session.Type == GAME_INTERNET &&
- !GameStatisticsPacketSent) {
- Register_Game_End_Time();
- Send_Statistics_Packet(); // Event - player aborted, and there were only 2 left.
- }
- }
- #endif //WIN32
- if (Debug_Print_Events) {
- if (DoList[j].Type==EventClass::EXIT) {
- printf("(%d) Executing EXIT, ID:%d (%s), EvFrame:%d\n",
- Frame,
- DoList[j].ID,
- (HouseClass::As_Pointer((HousesType)(DoList[j].ID)))->IniName,
- DoList[j].Frame);
- }
- }
- if (DoList[j].ID == PlayerPtr->ID) {
- DoList[j].Execute();
- } else if (DoList[j].Type==EventClass::EXIT) {
- //............................................................
- // If this EXIT event isn't from myself, destroy the connection
- // for that player. The HousesType for this event is the
- // connection ID.
- //............................................................
- if (Session.Type == GAME_MODEM ||
- Session.Type == GAME_NULL_MODEM) {
- Destroy_Null_Connection( house, 0 );
- } else if ((Session.Type == GAME_IPX ||
- Session.Type == GAME_INTERNET ||
- Session.Type == GAME_TEN ||
- Session.Type == GAME_MPATH) && net) {
- index = net->Connection_Index (house);
- if (index != -1) {
- for (k = index; k < net->Num_Connections() - 1; k++) {
- their_frame[k] = their_frame[k+1];
- their_sent[k] = their_sent[k+1];
- their_recv[k] = their_recv[k+1];
- }
- if (Session.Type == GAME_IPX ||
- Session.Type == GAME_INTERNET) {
- Destroy_Connection(house,0);
- }
- #if(TEN)
- else if (Session.Type == GAME_TEN) {
- Destroy_TEN_Connection(house,0);
- }
- #endif // TEN
- #if(MPATH)
- else if (Session.Type == GAME_MPATH) {
- Destroy_MPATH_Connection(house,0);
- }
- #endif // MPATH
- }
- }
- //
- // Special case for recording playback: turn the house over
- // to the computer.
- //
- if (Session.Play && DoList[j].Type==EventClass::EXIT) {
- hptr->IsHuman = false;
- hptr->IQ = Rule.MaxIQ;
- hptr->Computer_Paranoid();
- strcpy (hptr->IniName,Text_String(TXT_COMPUTER));
- Session.NumPlayers--;
- }
- }
- }
- //...............................................................
- // For a FRAMEINFO event, check the CRC value.
- //...............................................................
- else if (DoList[j].Type == EventClass::FRAMEINFO) {
- //............................................................
- // Skip the CRC check if we're less than 32 frames into the game;
- // this will prevent a newly-loaded modem game from instantly
- // going out of sync, if the games were saved at different
- // frame numbers.
- //............................................................
- if (!skip_crc || *skip_crc == 0) {
- check_crc = 1;
- }
- else {
- check_crc = 0;
- }
- if (check_crc
- && DoList[j].Frame == Frame
- && DoList[j].Data.FrameInfo.Delay < 32) {
- index = ((DoList[j].Frame - DoList[j].Data.FrameInfo.Delay) &
- 0x001f);
- if (CRC[index] != DoList[j].Data.FrameInfo.CRC) {
- Print_CRCs(&DoList[j]);
- #if(TEN)
- Send_TEN_Out_Of_Sync();
- #endif // TEN
- #if(MPATH)
- //Send_MPATH_Out_Of_Sync();
- #endif // MPATH
- if (WWMessageBox().Process (TXT_OUT_OF_SYNC,
- TXT_CONTINUE, TXT_STOP) == 0) {
- if (Session.Type == GAME_MODEM ||
- Session.Type == GAME_NULL_MODEM) {
- Destroy_Null_Connection( house, -1 );
- Shutdown_Modem();
- Session.Type = GAME_NORMAL;
- }
- else if ((Session.Type == GAME_IPX ||
- Session.Type == GAME_INTERNET) && net) {
- while (net->Num_Connections()) {
- Keyboard->Check();
- Destroy_Connection (net->Connection_ID(0), -1);
- }
- }
- #if(TEN)
- else if (Session.Type == GAME_TEN && net) {
- while (net->Num_Connections()) {
- Destroy_TEN_Connection (net->Connection_ID(0), -1);
- }
- }
- #endif
- #if(MPATH)
- else if (Session.Type == GAME_MPATH && net) {
- while (net->Num_Connections()) {
- Destroy_MPATH_Connection (net->Connection_ID(0), -1);
- }
- }
- #endif
- Map.Flag_To_Redraw(true);
- }
- else {
- return (0);
- }
- return (1);
- }
- }
- }
- //...............................................................
- // Execute other commands
- //...............................................................
- else {
- DoList[j].Execute();
- }
- //...............................................................
- // Mark this event as executed.
- //...............................................................
- DoList[j].IsExecuted = 1;
- #ifdef MIRROR_QUEUE
- MirrorList[j].IsExecuted = 1;
- #endif
- }
- }
- }
- return (1);
- } // end of Execute_DoList
- /***************************************************************************
- * Clean_DoList -- Cleans out old events from the DoList *
- * *
- * Currently, an event can only be removed from the DoList if it's at the *
- * head of the list; and event can't be removed from the middle. So, *
- * this routine loops as long as the next event in the DoList has been *
- * executed, it's removed. *
- * *
- * INPUT: *
- * net ptr to connection manager; ignored if NULL *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static void Clean_DoList(ConnManClass *net)
- {
- while (DoList.Count) {
- Keyboard->Check();
- if (net)
- Update_Queue_Mono (net, 7);
- //.....................................................................
- // Discard events that have been executed, OR it's too late to execute.
- // (This happens if another player exits the game; he'll leave FRAMEINFO
- // events lying around in my queue. They won't have been "executed",
- // because his IPX connection was destroyed.)
- //.....................................................................
- if ( (DoList.First().IsExecuted) || (Frame > DoList.First().Frame) ) {
- DoList.Next();
- #ifdef MIRROR_QUEUE
- MirrorList.Next();
- #endif
- }
- else {
- break;
- }
- }
- } // end of Clean_DoList
- /***************************************************************************
- * Queue_Record -- Records the DoList to disk *
- * *
- * This routine just saves any events in the DoList to disk; we can later *
- * "play back" the recording just be pulling events from disk rather than *
- * from the network! *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 08/14/1995 BRR : Created. *
- *=========================================================================*/
- static void Queue_Record(void)
- {
- int i,j;
- //------------------------------------------------------------------------
- // Compute # of events to save this frame
- //------------------------------------------------------------------------
- j = 0;
- for (i = 0; i < DoList.Count; i++) {
- if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) {
- j++;
- }
- }
- //------------------------------------------------------------------------
- // Save the # of events, then all events.
- //------------------------------------------------------------------------
- Session.RecordFile.Write (&j,sizeof(j));
- for (i = 0; i < DoList.Count; i++) {
- if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) {
- Session.RecordFile.Write (&DoList[i],sizeof (EventClass));
- j--;
- }
- }
- } /* end of Queue_Record */
- /***************************************************************************
- * Queue_Playback -- plays back queue entries from a record file *
- * *
- * This routine reads events from disk, putting them into the DoList; *
- * it then executes the DoList just like the network version does. The *
- * result is that the game "plays back" like a recording. *
- * *
- * This routine detects mouse motion and stops playback, so it can work *
- * like an "attract" mode, showing a demo of the game itself. *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 05/15/1995 BRR : Created. *
- *=========================================================================*/
- static void Queue_Playback(void)
- {
- int numevents;
- EventClass event;
- int i;
- int ok;
- static int mx,my;
- int max_houses;
- HousesType base_house;
- int key;
- int testframe;
- //------------------------------------------------------------------------
- // If the user hits ESC, stop the playback
- //------------------------------------------------------------------------
- if (Keyboard->Check()) {
- key = Keyboard->Get();
- if (key == KA_ESC || Session.Attract) {
- GameActive = 0;
- return;
- }
- }
- //------------------------------------------------------------------------
- // If we're in "Attract" mode, and the user moves the mouse, stop the
- // playback.
- //------------------------------------------------------------------------
- if (Session.Attract && Frame > 0 &&
- (mx != Get_Mouse_X() || my != Get_Mouse_Y())) {
- GameActive = 0;
- return;
- }
- mx = Get_Mouse_X();
- my = Get_Mouse_Y();
- //------------------------------------------------------------------------
- // Compute the Game's CRC
- //------------------------------------------------------------------------
- Compute_Game_CRC();
- CRC[Frame & 0x001f] = GameCRC;
- //------------------------------------------------------------------------
- // If we've reached the CRC print frame, do so & exit
- //------------------------------------------------------------------------
- if (Frame >= Session.TrapPrintCRC) {
- Print_CRCs(NULL);
- //Prog_End();
- Emergency_Exit(0);
- }
- //------------------------------------------------------------------------
- // Don't read anything the first time through (since the Queue_AI_Network
- // routine didn't write anything the first time through); do this after the
- // CRC is computed, since we'll still need a CRC for Frame 0.
- //------------------------------------------------------------------------
- if (Frame==0 && Session.Type!=GAME_NORMAL) {
- return;
- }
- //------------------------------------------------------------------------
- // Only process every 'FrameSendRate' frames
- //------------------------------------------------------------------------
- testframe = ((Frame + (Session.FrameSendRate - 1)) /
- Session.FrameSendRate) * Session.FrameSendRate;
- if ( (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) &&
- Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
- if (Frame != testframe) {
- return;
- }
- }
- //------------------------------------------------------------------------
- // Read the DoList from disk
- //------------------------------------------------------------------------
- ok = 1;
- if (Session.RecordFile.Read (&numevents, sizeof(numevents)) ==
- sizeof(numevents)) {
- for (i = 0; i < numevents; i++) {
- if (Session.RecordFile.Read (&event, sizeof(EventClass)) ==
- sizeof(EventClass)) {
- event.IsExecuted = 0;
- DoList.Add (event);
- #ifdef MIRROR_QUEUE
- MirrorList.Add(event);
- #endif
- }
- else {
- ok = 0;
- break;
- }
- }
- }
- else {
- ok = 0;
- }
- if (!ok) {
- GameActive = 0;
- return;
- }
- //------------------------------------------------------------------------
- // Execute the DoList; if an error occurs, bail out.
- //------------------------------------------------------------------------
- if (Session.Type == GAME_NORMAL) {
- max_houses = 1;
- base_house = PlayerPtr->Class->House;
- }
- else {
- max_houses = Session.MaxPlayers;
- base_house = HOUSE_MULTI1;
- }
- if (!Execute_DoList(max_houses, base_house, NULL, NULL, NULL, NULL, NULL)) {
- GameActive = 0;
- return;
- }
- //------------------------------------------------------------------------
- // Clean out the DoList
- //------------------------------------------------------------------------
- Clean_DoList(NULL);
- } /* end of Queue_Playback */
- /***************************************************************************
- * Compute_Game_CRC -- Computes a CRC value of the entire game. *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 05/09/1995 BRR : Created. *
- *=========================================================================*/
- static void Compute_Game_CRC(void)
- {
- int i,j;
- VesselClass *vessp;
- InfantryClass *infp;
- UnitClass *unitp;
- BuildingClass *bldgp;
- ObjectClass *objp;
- HouseClass *housep;
- GameCRC = 0;
- //------------------------------------------------------------------------
- // Infantry
- //------------------------------------------------------------------------
- for (i = 0; i < Infantry.Count(); i++) {
- infp = (InfantryClass *)Infantry.Active_Ptr(i);
- Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
- Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom);
- Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom);
- }
- //------------------------------------------------------------------------
- // Units
- //------------------------------------------------------------------------
- for (i = 0; i < Units.Count(); i++) {
- unitp = (UnitClass *)Units.Active_Ptr(i);
- Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing +
- (int)unitp->SecondaryFacing);
- }
- //------------------------------------------------------------------------
- // Shippies
- //------------------------------------------------------------------------
- for (i = 0; i < Vessels.Count(); i++) {
- vessp = (VesselClass *)Vessels.Active_Ptr(i);
- Add_CRC (&GameCRC, (int)vessp->Coord + (int)vessp->PrimaryFacing);
- Add_CRC (&GameCRC, (int)vessp->Speed + (int)vessp->NavCom);
- Add_CRC (&GameCRC, (int)vessp->Strength);
- Add_CRC (&GameCRC, (int)vessp->Mission + (int)vessp->TarCom);
- }
- //------------------------------------------------------------------------
- // Buildings
- //------------------------------------------------------------------------
- for (i = 0; i < Buildings.Count(); i++) {
- bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
- Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
- }
- //------------------------------------------------------------------------
- // Houses
- //------------------------------------------------------------------------
- for (i = 0; i < Houses.Count(); i++) {
- housep = (HouseClass *)Houses.Active_Ptr(i);
- Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power +
- (int)housep->Drain);
- }
- //------------------------------------------------------------------------
- // Map Layers
- //------------------------------------------------------------------------
- for (i = 0; i < LAYER_COUNT; i++) {
- for (j = 0; j < Map.Layer[i].Count(); j++) {
- objp = Map.Layer[i][j];
- Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
- }
- }
- //------------------------------------------------------------------------
- // Logic Layers
- //------------------------------------------------------------------------
- for (i = 0; i < Logic.Count(); i++) {
- objp = Logic[i];
- Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
- }
- //------------------------------------------------------------------------
- // A random #
- //------------------------------------------------------------------------
- // Add_CRC(&GameCRC, Scen.RandomNumber.Seed);
- Add_CRC(&GameCRC, Scen.RandomNumber);
- } /* end of Compute_Game_CRC */
- /***************************************************************************
- * Add_CRC -- Adds a value to a CRC *
- * *
- * INPUT: *
- * crc ptr to crc *
- * val value to add *
- * *
- * OUTPUT: *
- * none *
- * *
- * WARNINGS: *
- * none *
- * *
- * HISTORY: *
- * 05/09/1995 BRR : Created. *
- *=========================================================================*/
- void Add_CRC(unsigned long *crc, unsigned long val)
- {
- int hibit;
- if ( (*crc) & 0x80000000) {
- hibit = 1;
- }
- else {
- hibit = 0;
- }
- (*crc) <<= 1;
- (*crc) += val;
- (*crc) += hibit;
- } /* end of Add_CRC */
- /***************************************************************************
- * Print_CRCs -- Prints a data file for finding Sync Bugs *
- * *
- * INPUT: *
- * ev -- event to display *
- * *
- * OUTPUT: *
- * none *
- * *
- * WARNINGS: *
- * none *
- * *
- * HISTORY: *
- * 05/09/1995 BRR : Created. *
- *=========================================================================*/
- static void Print_CRCs(EventClass *ev)
- {
- int i,j;
- InfantryClass *infp;
- UnitClass *unitp;
- VesselClass *vesselp;
- BuildingClass *bldgp;
- ObjectClass *objp;
- FILE *fp;
- HouseClass *housep;
- HousesType house;
- int color;
- Mono_Clear_Screen();
- Mono_Set_Cursor (0,0);
- fp = fopen("OUT.TXT","wt");
- if (fp==NULL) {
- return;
- }
- for (i = 0; i < 32; i++) {
- fprintf(fp,"CRC[%d]=%x\n",i,CRC[i]);
- }
- //
- // Houses
- //
- for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
- GameCRC = 0;
- housep = HouseClass::As_Pointer (house);
- if (housep) {
- HousesType actlike = housep->ActLike;
- color = housep->RemapColor;
- fprintf(fp,"%s: IsHuman:%d Color:%s ID:%d ActLike:%s\n",
- housep->IniName,
- housep->IsHuman,
- ColorNames[color],
- housep->ID,
- HouseClass::As_Pointer(actlike)->Class->Name());
- Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power +
- (int)housep->Drain);
- Mono_Printf("House %s:%x\n",housep->Class->Name(),GameCRC);
- }
- }
- //
- // Infantry
- //
- for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
- housep = HouseClass::As_Pointer (house);
- if (housep) {
- GameCRC = 0;
- fprintf(fp,"-------------------- %s Infantry -------------------\n",
- housep->Class->Name());
- for (i = 0; i < Infantry.Count(); i++) {
- infp = (InfantryClass *)Infantry.Active_Ptr(i);
- if (infp->Owner()==house) {
- Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
- Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom);
- Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom);
- fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d Tgt:%x Speed:%d NavCom:%x\n",
- infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
- infp->Class->Type, infp->As_Target(), infp->Speed, infp->NavCom);
- }
- }
- Mono_Printf("%s Infantry:%x\n",housep->Class->Name(),GameCRC);
- }
- }
- //
- // Units
- //
- for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
- housep = HouseClass::As_Pointer (house);
- if (housep) {
- GameCRC = 0;
- fprintf(fp,"-------------------- %s Units -------------------\n",
- housep->Class->Name());
- for (i = 0; i < Units.Count(); i++) {
- unitp = (UnitClass *)Units.Active_Ptr(i);
- if (unitp->Owner()==house) {
- Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing +
- (int)unitp->SecondaryFacing);
- fprintf(fp,
- "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d Tgt:%x\n",
- unitp->Coord,(int)unitp->PrimaryFacing,
- (int)unitp->SecondaryFacing,unitp->Get_Mission(),
- unitp->Class->Type, unitp->As_Target());
- }
- }
- Mono_Printf("%s Units:%x\n",housep->Class->Name(),GameCRC);
- }
- }
- //
- // Vessels
- //
- for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
- housep = HouseClass::As_Pointer (house);
- if (housep) {
- GameCRC = 0;
- fprintf(fp,"-------------------- %s Vessels -------------------\n",
- housep->Class->Name());
- for (i = 0; i < Vessels.Count(); i++) {
- vesselp = (VesselClass *)Vessels.Active_Ptr(i);
- if (vesselp->Owner()==house) {
- Add_CRC (&GameCRC, (int)vesselp->Coord + (int)vesselp->PrimaryFacing);
- Add_CRC (&GameCRC, (int)vesselp->Speed + (int)vesselp->NavCom);
- Add_CRC (&GameCRC, (int)vesselp->Strength);
- Add_CRC (&GameCRC, (int)vesselp->Mission + (int)vesselp->TarCom);
- fprintf(fp,
- "COORD:%x Facing:%d Mission:%d Strength:%d Type:%d Tgt:%x\n",
- vesselp->Coord,(int)vesselp->PrimaryFacing,
- vesselp->Get_Mission(), vesselp->Strength,
- vesselp->Class->Type, vesselp->As_Target());
- }
- }
- Mono_Printf("%s Vessels:%x\n",housep->Class->Name(),GameCRC);
- }
- }
- //
- // Buildings
- //
- for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
- housep = HouseClass::As_Pointer (house);
- if (housep) {
- GameCRC = 0;
- fprintf(fp,"-------------------- %s Buildings -------------------\n",
- housep->Class->Name());
- for (i = 0; i < Buildings.Count(); i++) {
- bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
- if (bldgp->Owner()==house) {
- Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
- fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d Tgt:%x\n",
- bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
- bldgp->Class->Type, bldgp->As_Target());
- }
- }
- Mono_Printf("%s Buildings:%x\n",housep->Class->Name(),GameCRC);
- }
- }
- //
- // Animations
- //
- AnimClass *animp;
- fprintf(fp,"-------------------- Animations -------------------\n");
- for (i = 0; i < Anims.Count(); i++) {
- animp = (AnimClass *)Anims.Active_Ptr(i);
- fprintf(fp,"Target:%x OwnerHouse:%d Loops:%d\n",
- animp->xObject,
- animp->OwnerHouse,
- animp->Loops);
- }
- //------------------------------------------------------------------------
- // Map Layers
- //------------------------------------------------------------------------
- GameCRC = 0;
- for (i = 0; i < LAYER_COUNT; i++) {
- fprintf(fp,">>>> MAP LAYER %d <<<<\n",i);
- for (j = 0; j < Map.Layer[i].Count(); j++) {
- objp = Map.Layer[i][j];
- Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
- fprintf(fp,"Object %d: %x ",j,objp->Coord);
- if (objp->What_Am_I() == RTTI_AIRCRAFT)
- fprintf(fp,"Aircraft (Type:%d) ",
- (AircraftType)(*((AircraftClass *)objp)));
- else if (objp->What_Am_I() == RTTI_ANIM)
- fprintf(fp,"Anim (Type:%d) ",
- (AnimType)(*((AnimClass *)objp)));
- else if (objp->What_Am_I() == RTTI_BUILDING)
- fprintf(fp,"Building (Type:%d) ",
- (StructType)(*((BuildingClass *)objp)));
- else if (objp->What_Am_I() == RTTI_BULLET)
- fprintf(fp,"Bullet (Type:%d) ",
- (BulletType)(*((BulletClass *)objp)));
- else if (objp->What_Am_I() == RTTI_INFANTRY)
- fprintf(fp,"Infantry (Type:%d) ",
- (InfantryType)(*((InfantryClass *)objp)));
- else if (objp->What_Am_I() == RTTI_OVERLAY)
- fprintf(fp,"Overlay (Type:%d) ",
- (OverlayType)(*((OverlayClass *)objp)));
- else if (objp->What_Am_I() == RTTI_SMUDGE)
- fprintf(fp,"Smudge (Type:%d) ",
- (SmudgeType)(*((SmudgeClass *)objp)));
- else if (objp->What_Am_I() == RTTI_TEMPLATE)
- fprintf(fp,"Template (Type:%d) ",
- (TemplateType)(*((TemplateClass *)objp)));
- else if (objp->What_Am_I() == RTTI_TERRAIN)
- fprintf(fp,"Terrain (Type:%d) ",
- (TerrainType)(*((TerrainClass *)objp)));
- else if (objp->What_Am_I() == RTTI_UNIT)
- fprintf(fp,"Unit (Type:%d) ",
- (UnitType)(*((UnitClass *)objp)));
- else if (objp->What_Am_I() == RTTI_VESSEL)
- fprintf(fp,"Vessel (Type:%d) ",
- (VesselType)(*((VesselClass *)objp)));
- house = objp->Owner();
- if (house!=HOUSE_NONE) {
- housep = HouseClass::As_Pointer (house);
- fprintf(fp,"Owner: %s\n",housep->Class->IniName);
- }
- else {
- fprintf(fp,"Owner: NONE\n");
- }
- }
- }
- Mono_Printf("Map Layers:%x \n",GameCRC);
- //------------------------------------------------------------------------
- // Logic Layers
- //------------------------------------------------------------------------
- GameCRC = 0;
- fprintf(fp,">>>> LOGIC LAYER <<<<\n");
- for (i = 0; i < Logic.Count(); i++) {
- objp = Logic[i];
- Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
- fprintf(fp,"Object %d: %x ",i,objp->Coord);
- if (objp->What_Am_I() == RTTI_AIRCRAFT)
- fprintf(fp,"Aircraft (Type:%d) ",
- (AircraftType)(*((AircraftClass *)objp)));
- else if (objp->What_Am_I() == RTTI_ANIM)
- fprintf(fp,"Anim (Type:%d) ",
- (AnimType)(*((AnimClass *)objp)));
- else if (objp->What_Am_I() == RTTI_BUILDING)
- fprintf(fp,"Building (Type:%d) ",
- (StructType)(*((BuildingClass *)objp)));
- else if (objp->What_Am_I() == RTTI_BULLET)
- fprintf(fp,"Bullet (Type:%d) ",
- (BulletType)(*((BulletClass *)objp)));
- else if (objp->What_Am_I() == RTTI_INFANTRY)
- fprintf(fp,"Infantry (Type:%d) ",
- (InfantryType)(*((InfantryClass *)objp)));
- else if (objp->What_Am_I() == RTTI_OVERLAY)
- fprintf(fp,"Overlay (Type:%d) ",
- (OverlayType)(*((OverlayClass *)objp)));
- else if (objp->What_Am_I() == RTTI_SMUDGE)
- fprintf(fp,"Smudge (Type:%d) ",
- (SmudgeType)(*((SmudgeClass *)objp)));
- else if (objp->What_Am_I() == RTTI_TEMPLATE)
- fprintf(fp,"Template (Type:%d) ",
- (TemplateType)(*((TemplateClass *)objp)));
- else if (objp->What_Am_I() == RTTI_TERRAIN)
- fprintf(fp,"Terrain (Type:%d) ",
- (TerrainType)(*((TerrainClass *)objp)));
- else if (objp->What_Am_I() == RTTI_UNIT)
- fprintf(fp,"Unit (Type:%d) ",
- (UnitType)(*((UnitClass *)objp)));
- house = objp->Owner();
- if (house!=HOUSE_NONE) {
- housep = HouseClass::As_Pointer (house);
- fprintf(fp,"Owner: %s\n",housep->Class->IniName);
- }
- else {
- fprintf(fp,"Owner: NONE\n");
- }
- }
- Mono_Printf("Logic:%x \n",GameCRC);
- //------------------------------------------------------------------------
- // Random # generator, frame #
- //------------------------------------------------------------------------
- Mono_Printf("Random Number:%x \n",Scen.RandomNumber.Seed);
- #ifdef RANDOM_COUNT
- fprintf(fp,"\nRandom Number:%x (Count1:%d, Count2:%d)\n",
- Scen.RandomNumber.Seed,
- Scen.RandomNumber.Count1,
- Scen.RandomNumber.Count2);
- #else
- fprintf(fp,"\nRandom Number:%x\n",Scen.RandomNumber.Seed);
- #endif
- Mono_Printf("My Frame:%d \n",Frame);
- fprintf(fp,"My Frame:%d\n",Frame);
- if (ev) {
- fprintf(fp,"\n");
- fprintf(fp,"Offending event:\n");
- fprintf(fp," Type: %d\n",ev->Type);
- fprintf(fp," Frame: %d\n",ev->Frame);
- fprintf(fp," ID: %x\n",ev->ID);
- fprintf(fp," CRC: %x\n",ev->Data.FrameInfo.CRC);
- fprintf(fp," CommandCount: %d\n",ev->Data.FrameInfo.CommandCount);
- fprintf(fp," Delay: %d\n",ev->Data.FrameInfo.Delay);
- }
- fclose(fp);
- } /* end of Print_CRCs */
- /***************************************************************************
- * Init_Queue_Mono -- inits mono display *
- * *
- * This routine steals control of the mono screen away from the rest of *
- * the engine, by setting the global IsMono; if IsMono is set, the other *
- * routines in this module turn off the Mono display when they're done *
- * with it, so the rest of the engine won't over-write what we're writing. *
- * *
- * INPUT: *
- * net ptr to connection manager *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static void Init_Queue_Mono(ConnManClass *net)
- {
- #if(SHOW_MONO)
- //------------------------------------------------------------------------
- // Set 'IsMono' so we can steal the mono screen from the engine
- //------------------------------------------------------------------------
- if ((Frame==0 || Session.LoadGame) && MonoClass::Is_Enabled()) {
- IsMono = true;
- }
- //------------------------------------------------------------------------
- // Enable mono output for our stuff; we must Disable it before we return
- // control to the engine.
- //------------------------------------------------------------------------
- if (IsMono)
- MonoClass::Enable();
- if (net->Num_Connections() > 0) {
- //.....................................................................
- // Network mono debugging screen
- //.....................................................................
- if (NetMonoMode==0) {
- if (Frame==0 || Session.LoadGame || NewMonoMode) {
- net->Configure_Debug (0, sizeof (CommHeaderType),
- sizeof(EventClass::EventType), EventClass::EventNames, 0, 27);
- net->Mono_Debug_Print (0,1);
- NewMonoMode = 0;
- }
- else {
- net->Mono_Debug_Print (0,0);
- }
- }
- //.....................................................................
- // Flow control debugging output
- //.....................................................................
- else {
- if (NewMonoMode) {
- Mono_Clear_Screen();
- Mono_Printf(" Queue AI:\n"); // flowcount[0]
- Mono_Printf(" Build Packet Loop:\n"); // flowcount[1]
- Mono_Printf(" Frame Sync:\n"); // flowcount[2]
- Mono_Printf(" Frame Sync Resend:\n"); // flowcount[3]
- Mono_Printf(" Frame Sync Timeout:\n"); // flowcount[4]
- Mono_Printf(" Frame Sync New Message:\n"); // flowcount[5]
- Mono_Printf(" DoList Execution:\n"); // flowcount[6]
- Mono_Printf(" DoList Cleaning:\n"); // flowcount[7]
- Mono_Printf("\n");
- Mono_Printf(" Frame:\n");
- Mono_Printf(" Session.MaxAhead:\n");
- Mono_Printf(" their_recv:\n");
- Mono_Printf(" their_sent:\n");
- Mono_Printf(" my_sent:\n");
- NewMonoMode = 0;
- }
- }
- }
- #else
- net = net;
- #endif
- } // end of Init_Queue_Mono
- /***************************************************************************
- * Update_Queue_Mono -- updates mono display *
- * *
- * INPUT: *
- * net ptr to connection manager *
- * flow_index index # for flow-count updates *
- * -1: display *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static void Update_Queue_Mono(ConnManClass *net, int flow_index)
- {
- #if(SHOW_MONO)
- static int flowcount[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
- //------------------------------------------------------------------------
- // If 'NetMonoMode' is 1, display flowcount info
- //------------------------------------------------------------------------
- if (NetMonoMode==1) {
- if (flow_index >= 0 && flow_index < 20) {
- Mono_Set_Cursor(35,flow_index);
- flowcount[flow_index]++;
- Mono_Printf("%d",flowcount[flow_index]);
- }
- }
- //------------------------------------------------------------------------
- // Otherwise, display the connection debug screen
- //------------------------------------------------------------------------
- else {
- net->Mono_Debug_Print (0,0);
- }
- #else
- flow_index = flow_index;
- net = net;
- #endif
- } // end of Update_Queue_Mono
- /***************************************************************************
- * Print_Framesync_Values -- displays frame-sync variables *
- * *
- * INPUT: *
- * curframe current game Frame # *
- * max_ahead max-ahead value *
- * num_connections # connections *
- * their_recv # commands I've received from my connections *
- * their_sent # commands each connection claims to have sent *
- * my_sent # commands I've sent *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 11/21/1995 BRR : Created. *
- *=========================================================================*/
- static void Print_Framesync_Values(long curframe, unsigned long max_ahead,
- int num_connections, unsigned short *their_recv,
- unsigned short *their_sent, unsigned short my_sent)
- {
- #if(SHOW_MONO)
- int i;
- if (NetMonoMode==1) {
- Mono_Set_Cursor(35,9);
- Mono_Printf("%d",curframe);
- Mono_Set_Cursor(35,10);
- Mono_Printf("%d",max_ahead);
- for (i = 0; i < num_connections; i++) {
- Mono_Set_Cursor(35 + i*5,11);
- Mono_Printf("%4d",(int)their_recv[i]);
- }
- for (i = 0; i < num_connections; i++) {
- Mono_Set_Cursor(35 + i*5,12);
- Mono_Printf("%4d",(int)their_sent[i]);
- }
- Mono_Set_Cursor(35,13);
- Mono_Printf("%4d",(int)my_sent);
- }
- #else
- curframe = curframe;
- max_ahead = max_ahead;
- num_connections = num_connections;
- their_recv = their_recv;
- their_sent = their_sent;
- my_sent = my_sent;
- #endif
- } // end of Print_Framesync_Values
- /***************************************************************************
- * Dump_Packet_Too_Late_Stuff -- Dumps a debug file to disk *
- * *
- * INPUT: *
- * event ptr to event to print *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 06/28/1996 BRR : Created. *
- *=========================================================================*/
- void Dump_Packet_Too_Late_Stuff(EventClass *event, ConnManClass *net,
- long *their_frame, unsigned short *their_sent, unsigned short *their_recv)
- {
- FILE *fp;
- int i;
- HousesType house;
- fp = fopen("toolate.txt", "wt");
- if (!fp) {
- return;
- }
- fprintf(fp,"----------------- Event data: ----------------------\n");
- fprintf(fp,"Type: %s\n",EventClass::EventNames[event->Type]);
- fprintf(fp,"Frame: %d\n",event->Frame);
- fprintf(fp,"ID: %d\n",event->ID);
- for (i = 0; i < Session.Players.Count(); i++) {
- if (event->ID == Session.Players[i]->Player.ID) {
- fprintf(fp,"Player's Name: %s",Session.Players[i]->Name);
- }
- }
- fprintf(fp,"\n");
- fprintf(fp,"--------------------- My data: ---------------------\n");
- fprintf(fp,"My Frame:%d\n",Frame);
- fprintf(fp,"My MaxAhead:%d\n",Session.MaxAhead);
- if (net) {
- fprintf(fp,"-------------------- Frame Stats: ------------------\n");
- fprintf(fp,"Name ID TheirFrame TheirSent TheirRecv\n");
- for (i = 0; i < net->Num_Connections(); i++) {
- house = (HousesType)(net->Connection_ID(i));
- fprintf(fp,"%12s %2d %6d %6d %6d\n",
- (HouseClass::As_Pointer(house))->IniName,
- net->Connection_ID(i),
- their_frame[i],
- their_sent[i],
- their_recv[i]);
- }
- }
- fclose(fp);
- }
- /***************************************************************************
- * Check_Mirror -- Checks mirror memory *
- * *
- * INPUT: *
- * none. *
- * *
- * OUTPUT: *
- * none. *
- * *
- * WARNINGS: *
- * none. *
- * *
- * HISTORY: *
- * 10/14/1996 BRR : Created. *
- *=========================================================================*/
- void Check_Mirror(void)
- {
- #ifdef MIRROR_QUEUE
- int i;
- char txt[80];
- unsigned long *ptr;
- int found_5s = 0;
- ptr = (unsigned long *)(DoList.Get_Array());
- for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
- sizeof(unsigned long); i++) {
- if (ptr[i] == 0x55555555) {
- sprintf(txt,"55555555 found in DoList! Addr:%p", &(ptr[i]));
- WWMessageBox().Process (txt);
- found_5s = 1;
- }
- }
- ptr = (unsigned long *)(MirrorList.Get_Array());
- for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
- sizeof(unsigned long); i++) {
- if (ptr[i] == 0x55555555) {
- sprintf(txt,"55555555 found in MirrorList! Addr:%p", &(ptr[i]));
- WWMessageBox().Process (txt);
- found_5s = 1;
- }
- }
- ptr = (unsigned long *)(DoList.Get_Array());
- for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
- sizeof(unsigned long); i++) {
- if (ptr[i] == 0xAAAAAAAA) {
- sprintf(txt,"AAAAAAAA found in DoList! Addr:%p", &(ptr[i]));
- WWMessageBox().Process (txt);
- found_5s = 1;
- }
- }
- ptr = (unsigned long *)(MirrorList.Get_Array());
- for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
- sizeof(unsigned long); i++) {
- if (ptr[i] == 0xAAAAAAAA) {
- sprintf(txt,"AAAAAAAA found in MirrorList! Addr:%p", &(ptr[i]));
- WWMessageBox().Process (txt);
- found_5s = 1;
- }
- }
- for (i = 0; i < DoList.Count; i++) {
- if (memcmp(&DoList[i], &MirrorList[i], sizeof(EventClass)) != 0) {
- sprintf(txt,"Queue Memory Trashed! Head:%d Tail:%d, Addr:%p or %p",
- DoList.Get_Head(),
- DoList.Get_Tail(),
- DoList.Get_Array() + i,
- MirrorList.Get_Array() + i);
- WWMessageBox().Process (txt);
- //Prog_End();
- Emergency_Exit(0);
- }
- }
- if (found_5s) {
- //Prog_End();
- Emergency_Exit(0);
- }
- #endif
- } // end of Check_Mirror
- /*************************** end of queue.cpp ******************************/
|