FedSrv.CPP 395 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209
  1. /*-------------------------------------------------------------------------
  2. * fedsrv\FedSrv.CPP
  3. *
  4. * The main module of the server. Handles all communication and game logic
  5. *
  6. * Owner:
  7. *
  8. * Copyright 1986-1999 Microsoft Corporation, All Rights Reserved
  9. *-----------------------------------------------------------------------*/
  10. #include "pch.h"
  11. #include <..\TCLib\CoInit.h>
  12. #include <..\TCLib\AutoHandle.h>
  13. #include <agc.h>
  14. #include <zreg.h>
  15. #ifdef _DEBUG
  16. #include "FedSrvApp.h"
  17. #endif
  18. #if !defined(ALLSRV_STANDALONE)
  19. void DoDecrypt(int size, char* pdata)
  20. {
  21. DWORD encrypt = 0;
  22. //Do a rolling XOR to demunge the data
  23. for (int i = 0; (i < size); i += 4)
  24. {
  25. DWORD* p = (DWORD*)(pdata + size);
  26. encrypt = *p = *p ^ encrypt;
  27. }
  28. }
  29. static void DoEncrypt(int size, char* pdata)
  30. {
  31. DWORD encrypt = 0;
  32. //Do a rolling XOR to munge the data
  33. for (int i = 0; (i < size); i += 4)
  34. {
  35. DWORD* p = (DWORD*)(pdata + size);
  36. DWORD old = *p;
  37. *p = *p ^ encrypt;
  38. encrypt = old;
  39. }
  40. }
  41. #endif // !defined(ALLSRV_STANDALONE)
  42. ALLOC_MSG_LIST;
  43. Global g;
  44. #include "queries.h"
  45. #if defined(ALLSRV_STANDALONE)
  46. int GetNextCharacterID()
  47. {
  48. static int s_nID;
  49. return ++s_nID;
  50. }
  51. #endif // defined(ALLSRV_STANDALONE)
  52. #if !defined(ALLSRV_STANDALONE)
  53. #ifdef MOTHBALLED
  54. // Temporary hex stuff to get binary data from database
  55. void htoc(BYTE *c, BYTE b[])
  56. {
  57. *c = b[0] >= '0' && b[0] <= '9' ? b[0] - '0' : 10 + b[0] - 'A';
  58. *c <<= 4;
  59. *c |= b[1] >= '0' && b[1] <= '9' ? b[1] - '0' : 10 + b[1] - 'A';
  60. }
  61. void SzToBlob(BYTE * in, BYTE* pbBlob)
  62. {
  63. BYTE * pin = in;
  64. BYTE* pout = pbBlob;
  65. long i;
  66. int cbBlob = lstrlenA((char*)in) / 2;
  67. for (i = 0; i < cbBlob; i++, pin += 2, pout++)
  68. {
  69. htoc((BYTE *)pout, (BYTE *)pin);
  70. }
  71. }
  72. #endif // MOTHBALLED
  73. #endif // !defined(ALLSRV_STANDALONE)
  74. int TechBitFromToken(const char * szToken)
  75. {
  76. int itoken = 0;
  77. while (lstrcmpi(g.rgTechs[itoken], szToken))
  78. {
  79. if(itoken == cTechs)
  80. return NA;
  81. itoken++;
  82. }
  83. return itoken;
  84. }
  85. void TechsListToBits(BYTE * szTechs, TechTreeBitMask & ttbm)
  86. {
  87. ttbm.ClearAll();
  88. char * token;
  89. token = strtok((char *) szTechs, " ");
  90. while(token)
  91. {
  92. ttbm.SetBit(TechBitFromToken(token));
  93. token = strtok(NULL, " ");
  94. }
  95. }
  96. void LoadTechBits()
  97. {
  98. #if !defined(ALLSRV_STANDALONE)
  99. SQL_GO(TechBitsAll);
  100. while (SQL_SUCCEEDED(SQL_GETROW(TechBitsAll)))
  101. {
  102. // database ensures all bit ids are unique
  103. assert(TechBitsAll_BitID < cTechs);
  104. SqlStrCpy(g.rgTechs[TechBitsAll_BitID], TechBitsAll_Name, sizeof(TechBitsAll_Name));
  105. }
  106. #endif // !defined(ALLSRV_STANDALONE)
  107. }
  108. #if !defined(ALLSRV_STANDALONE)
  109. void LoadRankInfo()
  110. {
  111. SQL_GO(GetRankCount);
  112. SQL_GETROW(GetRankCount);
  113. g.cRankInfo = GetRankCount_Count;
  114. if (g.cRankInfo > 0)
  115. {
  116. g.vRankInfo = new RankInfo[g.cRankInfo];
  117. SQL_GO(GetRanks);
  118. for (int i = 0; i < g.cRankInfo; i++)
  119. {
  120. ZVerify(SQL_SUCCEEDED(SQL_GETROW(GetRanks)));
  121. g.vRankInfo[i].civ = GetRanks_CivID;
  122. g.vRankInfo[i].rank = GetRanks_Rank;
  123. g.vRankInfo[i].requiredRanking = GetRanks_Requirement;
  124. strcpy(g.vRankInfo[i].RankName, (char*)GetRanks_Name);
  125. }
  126. }
  127. }
  128. void UnloadRankInfo()
  129. {
  130. delete [] g.vRankInfo;
  131. g.cRankInfo = 0;
  132. }
  133. void LoadStaticMapInfo()
  134. {
  135. SQL_GO(GetStaticMapCount);
  136. SQL_GETROW(GetStaticMapCount);
  137. g.cStaticMapInfo = GetStaticMapCount_Count;
  138. if (g.cStaticMapInfo > 0)
  139. {
  140. g.vStaticMapInfo = new StaticMapInfo[g.cStaticMapInfo];
  141. SQL_GO(GetStaticMaps);
  142. for (int i = 0; i < g.cStaticMapInfo; i++)
  143. {
  144. ZVerify(SQL_SUCCEEDED(SQL_GETROW(GetStaticMaps)));
  145. SQLSTRCPY(g.vStaticMapInfo[i].cbIGCFile, GetStaticMaps_FileName);
  146. SQLSTRCPY(g.vStaticMapInfo[i].cbFriendlyName, GetStaticMaps_MapName);
  147. g.vStaticMapInfo[i].nNumTeams = GetStaticMaps_NumTeams;
  148. assert(GetStaticMaps_NumTeams > 1 && GetStaticMaps_NumTeams <= c_cSidesMax);
  149. }
  150. }
  151. }
  152. bool VerifyUserMap(const char* szMapFile, int nTeams)
  153. {
  154. for (int i = 0; i < g.cStaticMapInfo; i++)
  155. {
  156. if (nTeams == g.vStaticMapInfo[i].nNumTeams
  157. && 0 == strcmp(szMapFile, g.vStaticMapInfo[i].cbIGCFile))
  158. return true;
  159. }
  160. return false;
  161. }
  162. void UnloadStaticMapInfo()
  163. {
  164. delete [] g.vStaticMapInfo;
  165. g.cStaticMapInfo = 0;
  166. }
  167. #endif // !defined(ALLSRV_STANDALONE)
  168. /*-------------------------------------------------------------------------
  169. * AddNamedValue
  170. *-------------------------------------------------------------------------
  171. Purpose:
  172. Support function for SetSessionDetails to add a named value to a string
  173. Side Effects:
  174. modifies position pointer
  175. */
  176. void AddNamedValue(char ** ppNext, const char * szName, const char * szValue)
  177. {
  178. lstrcpy(*ppNext, szName);
  179. (*ppNext) += lstrlen(szName);
  180. *((*ppNext)++) = '\t';
  181. lstrcpy(*ppNext, szValue);
  182. (*ppNext) += lstrlen(szValue);
  183. *((*ppNext)++) = '\n';
  184. }
  185. /*-------------------------------------------------------------------------
  186. * SetSessionDetails
  187. *-------------------------------------------------------------------------
  188. Purpose:
  189. Generate a string of name/value pairs to attach to the session in FedMessaging
  190. Side Effects:
  191. Sets the session details in FedMessaging
  192. */
  193. void SetSessionDetails()
  194. {
  195. static char rgchBuf[10<<10]; // big buffer
  196. char * pNext = rgchBuf;
  197. static CTimer tt("setting session details", .01f);
  198. tt.Start();
  199. const ListFSMission * plistMission = CFSMission::GetMissions();
  200. for (LinkFSMission * plinkMission = plistMission->first(); plinkMission; plinkMission = plinkMission->next())
  201. {
  202. CFSMission * pfsMission = plinkMission->data();
  203. AddNamedValue(&pNext, "GAM", pfsMission->GetMissionDef()->misparms.strGameName);
  204. char szPlayers[5];
  205. _itoa(pfsMission->GetCountOfPlayers(NULL, false), szPlayers, 10);
  206. AddNamedValue(&pNext, "PLR", szPlayers);
  207. char szMaxPlayers[5];
  208. _itoa(pfsMission->GetMissionDef()->misparms.nTotalMaxPlayersPerGame, szMaxPlayers, 10);
  209. AddNamedValue(&pNext, "LIM", szMaxPlayers);
  210. }
  211. g.fm.SetSessionDetails(rgchBuf);
  212. tt.Stop();
  213. }
  214. /*-------------------------------------------------------------------------
  215. * UnloadDbCache
  216. *-------------------------------------------------------------------------
  217. * Purpose:
  218. * Clear out everything we got from database that doesn't change between missions
  219. */
  220. void UnloadDbCache()
  221. {
  222. #if !defined(ALLSRV_STANDALONE)
  223. if (g.trekCore)
  224. {
  225. g.trekCore->Terminate();
  226. delete g.trekCore;
  227. g.trekCore = NULL;
  228. }
  229. #endif // !defined(ALLSRV_STANDALONE)
  230. }
  231. /*-------------------------------------------------------------------------
  232. * FedSrv_Terminate
  233. *-------------------------------------------------------------------------
  234. * Purpose:
  235. * Cleanup before exiting
  236. *
  237. * Side Effects:
  238. * Everything that was allocated is deallocated and no longer valid
  239. */
  240. HRESULT pascal FedSrv_Terminate(void)
  241. {
  242. if (g.hReceiveThread)
  243. {
  244. // wake up receive thread and wait for it to quit
  245. SetEvent(g.hKillReceiveEvent);
  246. WaitForSingleObject(g.hReceiveThread, INFINITE);
  247. CloseHandle(g.hReceiveThread);
  248. g.hReceiveThread = NULL;
  249. }
  250. if (g.hKillReceiveEvent)
  251. {
  252. CloseHandle(g.hKillReceiveEvent);
  253. g.hKillReceiveEvent = NULL;
  254. }
  255. if (g.hPlayerEvent)
  256. {
  257. CloseHandle(g.hPlayerEvent);
  258. g.hPlayerEvent = NULL;
  259. }
  260. SWMRGDelete(&g.swmrg);
  261. // Turn everything off
  262. g.fm.Shutdown();
  263. DisconnectFromLobby();
  264. #if !defined(ALLSRV_STANDALONE)
  265. ShutDownSQL();
  266. #endif // !defined(ALLSRV_STANDALONE)
  267. timeEndPeriod(1);
  268. if (NULL != g.pServerCounters)
  269. {
  270. g.perfshare.FreeCounters(g.pServerCounters);
  271. g.pServerCounters = NULL;
  272. }
  273. g.perfshare.Terminate(); // this puts to rest the server and client instance perf counters
  274. _AGCModule.TriggerEvent(NULL, AllsrvEventID_Terminate, "", -1, -1, -1, 0);
  275. g.pzas = NULL;
  276. return(S_OK);
  277. }
  278. /*-------------------------------------------------------------------------
  279. * FetchGlobalAttributeSet
  280. *-------------------------------------------------------------------------
  281. * Purpose:
  282. * Fetch attributes from database for specified global attribute
  283. *
  284. * Parameters:
  285. * gaid: global attribute to get
  286. * gas: out parameter of fetched settings
  287. *
  288. * Side Effects:
  289. * Inner Database query
  290. */
  291. void FetchGlobalAttributeSet(GlobalAttributeID gaid, GlobalAttributeSet& gas)
  292. {
  293. #if !defined(ALLSRV_STANDALONE)
  294. gas.Initialize();
  295. if (gaid)
  296. {
  297. GlobalAttrs_ID = gaid;
  298. SQL_GO(GetGlobalAttrs);
  299. SQLRETURN sqlret = SQL_GETROW(GetGlobalAttrs);
  300. assert(SQL_SUCCEEDED(sqlret));
  301. #define DBTOGAS(attr) (1.0f + (float) (attr) / 100.0f)
  302. #define DBGSA(x) gas.SetAttribute(c_ga##x, DBTOGAS(GlobalAttrs_##x));
  303. DBGSA(MaxSpeed);
  304. DBGSA(Thrust);
  305. DBGSA(TurnRate);
  306. DBGSA(TurnTorque);
  307. DBGSA(MaxArmorStation);
  308. DBGSA(ArmorRegenerationStation);
  309. DBGSA(MaxShieldStation);
  310. DBGSA(ShieldRegenerationStation);
  311. DBGSA(MaxArmorShip);
  312. DBGSA(MaxShieldShip);
  313. DBGSA(ShieldRegenerationShip);
  314. DBGSA(ScanRange);
  315. DBGSA(Signature);
  316. DBGSA(MaxEnergy);
  317. DBGSA(SpeedAmmo);
  318. DBGSA(LifespanEnergy);
  319. DBGSA(TurnRateMissile);
  320. DBGSA(MiningRate);
  321. DBGSA(MiningYield);
  322. DBGSA(RipcordTime);
  323. DBGSA(DamageGuns);
  324. DBGSA(DamageMissiles);
  325. DBGSA(DevelopmentCost);
  326. DBGSA(DevelopmentTime);
  327. DBGSA(MiningCapacity);
  328. }
  329. #endif // !defined(ALLSRV_STANDALONE)
  330. }
  331. void JoinMission(CFSPlayer * pfsPlayer, DWORD dwCookie, const char * szPW)
  332. {
  333. // Get the mission in question
  334. CFSMission * pfsMission;
  335. if (NA == (int)dwCookie && CFSMission::GetMissions()->n() > 0)
  336. pfsMission = CFSMission::GetMissions()->first()->data();
  337. else
  338. pfsMission = CFSMission::GetMission(dwCookie);
  339. if (!pfsMission)
  340. {
  341. debugf("%s trying to join mission=%x that doesn't exist\n", pfsPlayer->GetName(), dwCookie);
  342. // tell them that they can't join
  343. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  344. END_PFM_CREATE
  345. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  346. pfmDelPosReq->iSide = NA;
  347. pfmDelPosReq->reason = DPR_NoMission;
  348. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  349. return;
  350. }
  351. if (pfsMission->GetStage() == STAGE_OVER)
  352. {
  353. // don't let a client join a paused game
  354. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  355. END_PFM_CREATE
  356. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  357. pfmDelPosReq->iSide = NA;
  358. pfmDelPosReq->reason = DPR_ServerPaused;
  359. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  360. return;
  361. }
  362. if (pfsMission->RequiresInvitation())
  363. {
  364. if (!pfsMission->IsInvited(pfsPlayer))
  365. {
  366. // one of the check to make sure client cannot log into games that it doesn't have access to
  367. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  368. END_PFM_CREATE
  369. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  370. pfmDelPosReq->iSide = NA;
  371. pfmDelPosReq->reason = DPR_PrivateGame;
  372. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  373. return;
  374. }
  375. }
  376. ImissionIGC * pMission = pfsMission->GetIGCMission();
  377. #if defined(ALLSRV_STANDALONE)
  378. if (g.fm.GetHostApplicationGuid() == FEDSRV_STANDALONE_PRIVATE_GUID)
  379. {
  380. //Disallow if the name is already in use
  381. ShipLinkIGC* psl;
  382. for (psl = pMission->GetShips()->first(); (psl != NULL); psl = psl->next())
  383. {
  384. CFSShip* pfsShip = (CFSShip *)(psl->data()->GetPrivateData());
  385. if (pfsShip->IsPlayer())
  386. {
  387. CFSPlayer* pfsPlayerOther = pfsShip->GetPlayer();
  388. if (_stricmp(pfsPlayer->GetName(), pfsPlayerOther->GetName()) == 0)
  389. {
  390. // one of the check to make sure client cannot log into games that it doesn't have access to
  391. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  392. END_PFM_CREATE
  393. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  394. pfmDelPosReq->iSide = NA;
  395. pfmDelPosReq->reason = DPR_DuplicateLogin;
  396. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  397. return;
  398. }
  399. }
  400. }
  401. }
  402. else
  403. #endif // defined(ALLSRV_STANDALONE)
  404. {
  405. #ifdef USEAUTH
  406. pfsMission->RemovePlayerByName(pfsPlayer->GetName(), QSR_DuplicateLocalLogon);
  407. #endif
  408. }
  409. const MissionParams* pmp = pMission->GetMissionParams();
  410. // if this mission has a player limit, check that.
  411. if (pmp->nTotalMaxPlayersPerGame != 0x7fff
  412. && pfsMission->GetCountOfPlayers(NULL, true) >= pmp->nTotalMaxPlayersPerGame)
  413. {
  414. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  415. END_PFM_CREATE
  416. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  417. pfmDelPosReq->iSide = NA;
  418. pfmDelPosReq->reason = DPR_GameFull;
  419. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  420. return;
  421. }
  422. // make sure this client is invited to the game
  423. const char* szPassword = pmp->strGamePassword;
  424. if (szPassword[0] != '\0'
  425. && Strcmp(szPassword, szPW) != 0)
  426. {
  427. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  428. END_PFM_CREATE
  429. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  430. pfmDelPosReq->iSide = NA;
  431. pfmDelPosReq->reason = DPR_BadPassword;
  432. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  433. return;
  434. }
  435. if (!pfsPlayer->CanCheat())
  436. {
  437. // make sure the player meets the rank requirements for this mission
  438. RankID rank = pfsPlayer->GetPersistPlayerScore(NA)->GetRank();
  439. if (pfsMission->GetMissionDef()->misparms.iMinRank > rank
  440. || pfsMission->GetMissionDef()->misparms.iMaxRank < rank)
  441. {
  442. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  443. END_PFM_CREATE
  444. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  445. pfmDelPosReq->iSide = NA;
  446. pfmDelPosReq->reason = DPR_InvalidRank;
  447. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  448. return;
  449. }
  450. }
  451. // check to see if the mission is running and whether joiners are allowed
  452. OldPlayerLink* popi = pfsMission->GetOldPlayerLink(pfsPlayer->GetName());
  453. SideID iSide;
  454. unsigned char bannedSideMask;
  455. if (popi)
  456. {
  457. if (popi->data().pso.GetDeaths() > pmp->iLives)
  458. {
  459. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  460. END_PFM_CREATE
  461. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  462. pfmDelPosReq->iSide = NA;
  463. pfmDelPosReq->reason = DPR_OutOfLives;
  464. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  465. return;
  466. }
  467. //Has this person been banned from the game (or ... is there a team that
  468. //will accept him)
  469. bannedSideMask = popi->data().bannedSideMask;
  470. //So ... if there are two teams, the legal side mask == 1 << 2 - 1 = 3
  471. unsigned char legalSideMask = SideMask(pMission->GetSides()->n()) - 1;
  472. if ((bannedSideMask & legalSideMask) == legalSideMask)
  473. {
  474. //No one wants this player
  475. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  476. END_PFM_CREATE
  477. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  478. pfmDelPosReq->iSide = NA;
  479. pfmDelPosReq->reason = DPR_Banned;
  480. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  481. return;
  482. }
  483. if ((popi->data().sideID < 0) || (bannedSideMask & SideMask(popi->data().sideID))
  484. || ((pfsMission->GetStage() == STAGE_STARTED) && pmp->bAllowDefections)
  485. || (popi->data().sideID >= pmp->nTeams))
  486. {
  487. iSide = SIDE_TEAMLOBBY;
  488. }
  489. else
  490. {
  491. iSide = popi->data().sideID;
  492. }
  493. }
  494. else
  495. {
  496. if (((!pmp->bAllowJoiners) && (pfsMission->GetStage() != STAGE_NOTSTARTED))
  497. || pmp->bLockLobby)
  498. {
  499. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  500. END_PFM_CREATE
  501. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  502. pfmDelPosReq->iSide = NA;
  503. pfmDelPosReq->reason = pmp->bLockLobby ? DPR_LobbyLocked : DPR_NoJoiners;
  504. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  505. return;
  506. }
  507. bannedSideMask = 0;
  508. if (pfsMission->GetStage() == STAGE_NOTSTARTED)
  509. iSide = NA; //Pick a random team
  510. else
  511. iSide = SIDE_TEAMLOBBY;
  512. }
  513. // assign a random side (if we can)
  514. if ((iSide == NA) || ((iSide == SIDE_TEAMLOBBY) && pmp->bLockSides))
  515. {
  516. iSide = pfsMission->PickNewSide(pfsPlayer, true, bannedSideMask);
  517. assert (iSide != NA);
  518. }
  519. // put them on the team lobby side
  520. pfsMission->AddPlayerToMission(pfsPlayer);
  521. IsideIGC* psideReq = pMission->GetSide(iSide);
  522. if (!psideReq)
  523. {
  524. assert(false);
  525. psideReq = pMission->GetSide(SIDE_TEAMLOBBY);
  526. }
  527. if (psideReq->GetObjectID() != SIDE_TEAMLOBBY)
  528. pfsMission->RequestPosition(pfsPlayer, psideReq, popi != NULL);
  529. }
  530. /*-------------------------------------------------------------------------
  531. * GotInvitationList
  532. *-------------------------------------------------------------------------
  533. Purpose:
  534. Callback from completion of CQGetInvitationList query.
  535. Side Effects:
  536. Players/teams added to list
  537. */
  538. void GotInvitationList(CQGetInvitationList * pquery)
  539. {
  540. CQGetInvitationListData * pqd = pquery->GetData();
  541. CFSMission * pfsMission = CFSMission::GetMissionFromIGCMissionID(pqd->missionID);
  542. int cRows = 0;
  543. if (pfsMission) // if the mission is gone, forget about it
  544. {
  545. CQGetInvitationListData * pRows = pquery->GetOutputRows(&cRows); // all the rows are here
  546. while (cRows--)
  547. {
  548. pfsMission->AddInvitation(SideID(pRows->iTeam), pRows->szSubject);
  549. }
  550. }
  551. }
  552. /*-------------------------------------------------------------------------
  553. * GotCharSquads
  554. *-------------------------------------------------------------------------
  555. Purpose:
  556. Callback from completion of CQCharSquads query.
  557. Side Effects:
  558. Tell the players what squads they're in.
  559. */
  560. void GotCharSquads(CQCharSquads * pquery)
  561. {
  562. CQCharSquadsData * pqd = pquery->GetData();
  563. CFMConnection * pcnxn = g.fm.GetConnectionFromId(pqd->dwConnectionID);
  564. if (!pcnxn)
  565. return; // nothing we do for someone who's gone
  566. CFSPlayer * pfsPlayer = CFSPlayer::GetPlayerFromConnection(*pcnxn);
  567. if (!pfsPlayer)
  568. return; // nothing we do for someone who's going away
  569. int cRows;
  570. CQCharSquadsData * pRows = pquery->GetOutputRows(&cRows); // all the rows are here
  571. while (cRows--)
  572. {
  573. pfsPlayer->GetSquadMembershipList()->last(
  574. new SquadMembership(
  575. pRows->squadID,
  576. (const char*)pRows->szSquadName,
  577. 0 == pRows->status,
  578. 1 == pRows->status && 1 == pRows->detailedStatus
  579. ));
  580. pRows++;
  581. }
  582. // tell them which squads they are on
  583. int nNumSquads = pfsPlayer->GetSquadMembershipList()->n();
  584. SquadMembership* vsquadmembership =
  585. (SquadMembership*)_alloca(sizeof(SquadMembership) * nNumSquads + 1);
  586. SquadMembershipLink* pSquadLink = pfsPlayer->GetSquadMembershipList()->first();
  587. for (int iSquad = 0; iSquad < nNumSquads; ++iSquad, pSquadLink = pSquadLink->next())
  588. {
  589. vsquadmembership[iSquad] = *(pSquadLink->data());
  590. }
  591. BEGIN_PFM_CREATE(g.fm, pfmSquadMemberships, LS, SQUAD_MEMBERSHIPS)
  592. FM_VAR_PARM(nNumSquads ? vsquadmembership : NULL, sizeof(SquadMembership) * nNumSquads)
  593. END_PFM_CREATE
  594. pfmSquadMemberships->cSquadMemberships = nNumSquads;
  595. g.fm.SendMessages(pcnxn, FM_GUARANTEED, FM_FLUSH);
  596. if (pqd->fJoin) // we waited for the squads because it's a squad game
  597. JoinMission(pfsPlayer, pqd->dwCookie, pqd->szPassword);
  598. }
  599. /*-------------------------------------------------------------------------
  600. * GotLogonDetails
  601. *-------------------------------------------------------------------------
  602. Purpose:
  603. Callback from GetLogonStats query. Logon finishes here
  604. */
  605. void GotLogonDetails(CQLogonStats * pquery)
  606. {
  607. CQLogonStatsData * pqd = pquery->GetData();
  608. CFMConnection * pcnxn = g.fm.GetConnectionFromId(pqd->dwConnectionID);
  609. bool fJoinNow = true; // only false if it's a squad game
  610. if (!pcnxn)
  611. return; // nothing we do for someone who's gone
  612. CFSPlayer * pfsPlayer = CFSPlayer::GetPlayerFromConnection(*pcnxn);
  613. bool fValid = pqd->fValid;
  614. bool fRetry = pqd->fRetry;
  615. // Some debugging stuff to monitor the amount of startup infowe send everyone
  616. g.cMsgsToOthers =
  617. g.cBytesToOthers = 0;
  618. float flDTime;
  619. int cMsgsOdometer;
  620. int cBytesOdometer;
  621. g.fm.CheckOdometer(flDTime, cMsgsOdometer, cBytesOdometer);
  622. if (fValid) // doesn't matter whether we created a new ship, or continued an existing one
  623. {
  624. lstrcpy(pqd->szReason, pqd->szCharacterName);
  625. if (NULL != g.pServerCounters)
  626. {
  627. g.pServerCounters->cLogins++;
  628. g.pServerCounters->cPlayersOnline++;
  629. }
  630. // Create the CFSPlayer object. We require that a player always have a ship, so create that first
  631. // Any ol' ship will do since the client doesn't see it anyway until they buy one
  632. // But it has to be free so they don't get a cash back bonus when they trade it in for a new ship
  633. DataShipIGC dataship;
  634. ZeroMemory(&dataship, sizeof(dataship));
  635. dataship.shipID = NA;
  636. dataship.sideID = NA;
  637. dataship.hullID = NA;
  638. //dataship.wingID = 1;
  639. /*
  640. #ifdef _ALLEGIANCE_DEV_
  641. pqd->fCanCheat = true;
  642. #endif
  643. */
  644. dataship.pilotType = pqd->fCanCheat ? c_ptCheatPlayer : c_ptPlayer;
  645. dataship.abmOrders = 0;
  646. dataship.baseObjectID = NA;
  647. lstrcpy(dataship.name, pqd->szReason);
  648. TRef<IshipIGC> pship = (IshipIGC *) g.trekCore->CreateObject(g.timeNow,
  649. OT_ship, &dataship, sizeof(dataship));
  650. pfsPlayer = new CFSPlayer(pcnxn, pqd->characterID, pqd->szCDKey, pship, !! pqd->fCanCheat);
  651. assert(pfsPlayer);
  652. #if !defined(ALLSRV_STANDALONE)
  653. PlayerScoreObject * plrsc = pfsPlayer->GetPlayerScoreObject();
  654. int cRows;
  655. CQLogonStatsData * pRows = pquery->GetOutputRows(&cRows); // all the rows are here
  656. while (cRows--)
  657. {
  658. PersistPlayerScoreObject * ppso = new PersistPlayerScoreObject(
  659. pRows->score,
  660. RATING_EXT2INT(pRows->rating),
  661. pRows->rank,
  662. pRows->civID);
  663. pfsPlayer->GetPersistPlayerScoreObjectList()->last(ppso);
  664. pRows++;
  665. }
  666. // no persist object is active now; will be when they go to a side
  667. #endif // !defined(ALLSRV_STANDALONE)
  668. }
  669. else
  670. {
  671. if (NULL != g.pServerCounters)
  672. g.pServerCounters->cLoginsFailed++;
  673. }
  674. BEGIN_PFM_CREATE(g.fm, pfmLogonAck, S, LOGONACK)
  675. FM_VAR_PARM(pqd->szReason, CB_ZTS)
  676. END_PFM_CREATE
  677. pfmLogonAck->shipID = fValid ? pfsPlayer->GetShipID() : 0;
  678. pfmLogonAck->fValidated = fValid;
  679. pfmLogonAck->fRetry = fRetry;
  680. pfmLogonAck->cookie = fValid ? pfsPlayer->GetCookie() : 0;
  681. pfmLogonAck->timeServer = g.timeNow;
  682. #if !defined(ALLSRV_STANDALONE)
  683. if (fValid)
  684. {
  685. // send the rank info for this server
  686. BEGIN_PFM_CREATE(g.fm, pfmRankInfo, S, RANK_INFO)
  687. FM_VAR_PARM(g.vRankInfo, sizeof(RankInfo) * g.cRankInfo)
  688. END_PFM_CREATE
  689. pfmRankInfo->cRanks = g.cRankInfo;
  690. // send the static map info for the maps on this server
  691. BEGIN_PFM_CREATE(g.fm, pfmStaticMapInfo, S, STATIC_MAP_INFO)
  692. FM_VAR_PARM(g.vStaticMapInfo, sizeof(StaticMapInfo) * g.cStaticMapInfo)
  693. END_PFM_CREATE
  694. // Get their squad memberships, but whether we need it b4 joining depends on whether it's a squad game
  695. CQCharSquads * pqueryCharSquads = new CQCharSquads(GotCharSquads);
  696. CQCharSquadsData * pqdSquads = pqueryCharSquads->GetData();
  697. pqdSquads->characterID = pqd->characterID;
  698. pqdSquads->dwConnectionID = pqd->dwConnectionID;
  699. CFSMission * pfsMission = CFSMission::GetMission(pqd->dwCookie);
  700. if (pfsMission) // it MAY have gone away in the meantime
  701. {
  702. pqdSquads->fJoin = pfsMission->IsSquadGame();
  703. fJoinNow = !pqdSquads->fJoin; // we EITHER join now, or later, not both
  704. pqdSquads->dwCookie = pqd->dwCookie;
  705. lstrcpy(pqdSquads->szPassword, pqd->szPassword);
  706. g.sql.PostQuery(pqueryCharSquads);
  707. }
  708. }
  709. #endif
  710. g.fm.SendMessages(pcnxn, FM_GUARANTEED, FM_FLUSH);
  711. g.fm.CheckOdometer(flDTime, cMsgsOdometer, cBytesOdometer);
  712. debugf("Player %s logon %s. Sent %d msgs and %dB to them in %1.3fs, "
  713. "and %d msgs and %dB to others in the process.\n",
  714. pqd->szCharacterName, fValid ? "succeeded" : "failed",
  715. cMsgsOdometer, cBytesOdometer, flDTime, g.cMsgsToOthers, g.cBytesToOthers);
  716. if (fValid && fJoinNow)
  717. JoinMission(pfsPlayer, pqd->dwCookie, pqd->szPassword);
  718. }
  719. /*-------------------------------------------------------------------------
  720. * SetCharStats
  721. *-------------------------------------------------------------------------
  722. Purpose:
  723. Upload character persisted stats
  724. Parameters:
  725. PersistPlayerScoreObject
  726. */
  727. void SetCharStats(int characterID,
  728. CFSPlayer* pfsPlayer,
  729. IsideIGC * pSide,
  730. PlayerScoreObject& pso,
  731. CFSMission* pfsMission)
  732. {
  733. if (0.0f == pso.GetTimePlayed())
  734. {
  735. return;
  736. }
  737. assert(pso.GetScore() >= 0.0f);
  738. assert (pSide);
  739. CivID civID = pSide->GetCivilization()->GetObjectID();
  740. PersistPlayerScoreObject& ppso = pso.GetPersist();
  741. RankID newRank = ppso.GetRank();
  742. if (pfsMission->GetScoresCount())
  743. {
  744. float score = ppso.GetScore() + pso.GetScore();
  745. int iScore = int(score);
  746. for (int i = g.cRankInfo - 1; (i >= 0); i--)
  747. {
  748. RankInfo& ri = g.vRankInfo[i];
  749. if ((ri.civ == civID) && (ri.requiredRanking < iScore))
  750. {
  751. newRank = ri.rank;
  752. break;
  753. }
  754. }
  755. // Update the persist player score object
  756. ppso.Set(score,
  757. pso.GetCombatRating(),
  758. newRank);
  759. // Save to the cached persist player score object as well
  760. if (pfsPlayer)
  761. {
  762. pfsPlayer->GetPersistPlayerScore(civID)->Set(score, pso.GetCombatRating(), newRank);
  763. //Update NA civ rank as well.
  764. PersistPlayerScoreObject* ppsoNA = pfsPlayer->GetPersistPlayerScore(NA);
  765. float s = ppsoNA->GetScore() + pso.GetScore();
  766. int is = int(s);
  767. for (int i = g.cRankInfo - 1; (i >= 0); i--)
  768. {
  769. RankInfo& ri = g.vRankInfo[i];
  770. if ((ri.civ == NA) && (ri.requiredRanking < is))
  771. {
  772. ppsoNA->Set(s, 0.0f, ri.rank);
  773. break;
  774. }
  775. }
  776. }
  777. }
  778. #if !defined(ALLSRV_STANDALONE)
  779. CQCharStats * pquery = new CQCharStats(NULL); // don't need call-back notification
  780. CQCharStatsData * pdq = pquery->GetData();
  781. pdq->CharacterID = characterID;
  782. pdq->CivID = civID;
  783. // All of these are for THIS GAME ONLY (except Rating, which is always overall)
  784. pdq->Rating = RATING_INT2EXT(pso.GetCombatRating());
  785. pdq->WarpsSpotted = pso.GetWarpsSpotted();
  786. pdq->AsteroidsSpotted = pso.GetAsteroidsSpotted();
  787. pdq->TechsRecovered = pso.GetTechsRecovered();
  788. pdq->MinerKills = pso.GetMinerKills();
  789. pdq->BuilderKills = pso.GetBuilderKills();
  790. pdq->LayerKills = pso.GetLayerKills();
  791. pdq->PlayerKills = pso.GetPlayerKills();
  792. pdq->BaseKills = pso.GetBaseKills();
  793. pdq->BaseCaptures = pso.GetBaseCaptures();
  794. pdq->Deaths = pso.GetDeaths();
  795. pdq->PilotBaseKills = pso.GetPilotBaseKills();
  796. pdq->PilotBaseCaptures = pso.GetPilotBaseCaptures();
  797. pdq->Minutes = pso.GetTimePlayed() / 60.0f;
  798. pdq->bWin = pso.GetWinner();
  799. pdq->bLose = pso.GetLoser();
  800. pdq->bWinCmd = pso.GetCommandWinner();
  801. pdq->bLoseCmd = pso.GetCommandLoser();
  802. pdq->Score = pso.GetScore();
  803. pdq->bScoresCount = pfsMission->GetScoresCount();
  804. pdq->RankOld = newRank;
  805. debugf("SetCharStats: bScoresCount=%d, CharacterID=%d, CivID=%d, Rating=%d,\n",
  806. pdq->bScoresCount,pdq->CharacterID, pdq->CivID, pdq->Rating);
  807. debugf(" WarpsSpotted=%.2f, AsteroidsSpotted=%.2f, TechsRecovered=%.2f,\n",
  808. pdq->WarpsSpotted, pdq->AsteroidsSpotted, pdq->TechsRecovered);
  809. debugf(" MinerKills=%.2f, BuilderKills=%.2f, LayerKills=%.2f, PlayerKills=%.2f,\n",
  810. pdq->MinerKills, pdq->BuilderKills, pdq->LayerKills, pdq->PlayerKills);
  811. debugf(" BaseKills=%.2f, BaseCaptures=%.2f, Deaths=%d, PilotBaseKills=%d, BaseCaptures=%d,\n",
  812. pdq->BaseKills, pdq->BaseCaptures, pdq->Deaths, pdq->PilotBaseKills, pdq->PilotBaseCaptures);
  813. debugf(" Minutes=%d, bWin=%d, bLose=%d, bWinCmd=%d, bLoseCmd=%d, RankOld=%d, Score=%.2f.\n",
  814. pdq->Minutes, pdq->bWin, pdq->bLose, pdq->bWinCmd, pdq->bLoseCmd, pdq->RankOld, pdq->Score);
  815. /* stress test
  816. int i1 = 0;
  817. for (i1 = 0; i1 < 10000; i1++)
  818. {
  819. CQCharStats * pq = (CQCharStats *) pquery->Copy(NULL);
  820. g.sql.PostQuery(pq);
  821. }
  822. */
  823. g.sql.PostQuery(pquery);
  824. #endif // !defined(ALLSRV_STANDALONE)
  825. }
  826. /*-------------------------------------------------------------------------
  827. * RecordSquadGame
  828. *-------------------------------------------------------------------------
  829. Purpose:
  830. Upload the results of a squad game
  831. Parameters:
  832. List of sides in the game & the winning side
  833. */
  834. void RecordSquadGame(const SideListIGC* psides, IsideIGC* psideWon)
  835. {
  836. #if !defined(ALLSRV_STANDALONE)
  837. CQReportSquadGame * pquery = new CQReportSquadGame(NULL);
  838. CQReportSquadGameData * pqd = pquery->GetData();
  839. pqd->squadIDWon = psideWon->GetSquadID();
  840. // fill in the losers
  841. // Blech - have to unroll the loop because we need to fill in different
  842. // variable names, and not just different indices
  843. SideLinkIGC* linkSide = psides->first();
  844. // loser 1
  845. {
  846. while (linkSide
  847. && (linkSide->data() == psideWon || linkSide->data()->GetSquadID() == NA))
  848. {
  849. linkSide = linkSide->next();
  850. }
  851. if (linkSide)
  852. {
  853. pqd->squadIDLost1 = linkSide->data()->GetSquadID();
  854. linkSide = linkSide->next();
  855. }
  856. else
  857. pqd->squadIDLost1 = NA;
  858. }
  859. // loser 2
  860. {
  861. while (linkSide
  862. && (linkSide->data() == psideWon || linkSide->data()->GetSquadID() == NA))
  863. {
  864. linkSide = linkSide->next();
  865. }
  866. if (linkSide)
  867. {
  868. pqd->squadIDLost2 = linkSide->data()->GetSquadID();
  869. linkSide = linkSide->next();
  870. }
  871. else
  872. pqd->squadIDLost2 = NA;
  873. }
  874. // loser 3
  875. {
  876. while (linkSide
  877. && (linkSide->data() == psideWon || linkSide->data()->GetSquadID() == NA))
  878. {
  879. linkSide = linkSide->next();
  880. }
  881. if (linkSide)
  882. {
  883. pqd->squadIDLost3 = linkSide->data()->GetSquadID();
  884. linkSide = linkSide->next();
  885. }
  886. else
  887. pqd->squadIDLost3 = NA;
  888. }
  889. // loser 4
  890. {
  891. while (linkSide
  892. && (linkSide->data() == psideWon || linkSide->data()->GetSquadID() == NA))
  893. {
  894. linkSide = linkSide->next();
  895. }
  896. if (linkSide)
  897. {
  898. pqd->squadIDLost4 = linkSide->data()->GetSquadID();
  899. linkSide = linkSide->next();
  900. }
  901. else
  902. pqd->squadIDLost4 = NA;
  903. }
  904. // loser 5
  905. {
  906. while (linkSide
  907. && (linkSide->data() == psideWon || linkSide->data()->GetSquadID() == NA))
  908. {
  909. linkSide = linkSide->next();
  910. }
  911. if (linkSide)
  912. {
  913. pqd->squadIDLost5 = linkSide->data()->GetSquadID();
  914. linkSide = linkSide->next();
  915. }
  916. else
  917. pqd->squadIDLost5 = NA;
  918. }
  919. assert(linkSide == NULL || linkSide->data() == psideWon && linkSide->next() == NULL);
  920. g.sql.PostQuery(pquery);
  921. #endif // !defined(ALLSRV_STANDALONE)
  922. }
  923. /*-------------------------------------------------------------------------
  924. * GetInvitations
  925. *-------------------------------------------------------------------------
  926. */
  927. void GetInvitations(int nInvitationID, MissionID missionID)
  928. {
  929. #if !defined(ALLSRV_STANDALONE)
  930. CQGetInvitationList * pquery = new CQGetInvitationList(GotInvitationList);
  931. CQGetInvitationListData * pdq = pquery->GetData();
  932. pdq->listid = nInvitationID;
  933. pdq->missionID = missionID;
  934. g.sql.PostQuery(pquery);
  935. #endif
  936. }
  937. /*-------------------------------------------------------------------------
  938. * LoadDbCache
  939. *-------------------------------------------------------------------------
  940. * Purpose:
  941. * Loads everything from database that's (basically) static for duration
  942. * Keeps both the igc data, and also snapshots of the messages so that they
  943. * can be sent to clients without any processing
  944. *
  945. * Returns:
  946. * nothing (yet)
  947. *
  948. * Side Effects:
  949. * lots of memory allocated for cached tables. Freed on cleanup
  950. */
  951. void LoadDbCache()
  952. {
  953. #if !defined(ALLSRV_STANDALONE)
  954. SQLRETURN sqlret = 0;
  955. int i = 0;
  956. int nCount;
  957. g.trekCore = CreateMission(); // this isn't a *real* mission, just a gateway to static stuff
  958. g.trekCore->Initialize(Time::Now(), &g.siteFedSrv);
  959. g.trekCore->SetStaticCore(NULL);
  960. // There's a number of database sizes that are greater than the message sizes,
  961. // and I don't want to put casts all over the place, so...
  962. #pragma warning(disable : 4244)
  963. LoadTechBits();
  964. // Load all ship types
  965. SQL_GO(ShipsCount);
  966. SQL_GETROW(ShipsCount);
  967. // to preserve DB ordering of ship types, get the ids in database order
  968. ShipID* vnShipIdList = (ShipID*)_alloca(g_cShipTypes * sizeof(ShipID));
  969. nCount = 0;
  970. SQL_GO(ShipTypeIDs);
  971. while (SQL_SUCCEEDED(sqlret = SQL_GETROW(ShipTypeIDs)))
  972. {
  973. vnShipIdList[nCount] = ShipTypeIDs_ShipTypeID;
  974. ++nCount;
  975. assert(nCount <= g_cShipTypes);
  976. }
  977. // Load all projectiles
  978. SQL_GO(Projectiles);
  979. while (SQL_SUCCEEDED(SQL_GETROW(Projectiles)))
  980. {
  981. DataProjectileTypeIGC dpt;
  982. dpt.projectileTypeID = Projectiles_ProjectileID;
  983. dpt.power = Projectiles_HitPointsInflict;
  984. dpt.blastPower = Projectiles_BlastPower;
  985. dpt.blastRadius = Projectiles_BlastRadius;
  986. dpt.speed = Projectiles_SpeedMax;
  987. dpt.absoluteF = false != Projectiles_IsAbsoluteSpeed;
  988. dpt.modelName[0] = '\0';
  989. SQLSTRCPY(dpt.textureName, Projectiles_FileTexture);
  990. SQLSTRCPY(dpt.modelName, Projectiles_FileModel);
  991. dpt.lifespan = Projectiles_TimeDuration / 1000.0f;
  992. dpt.radius = Projectiles_Size_cm / 100.0f; // centimeters to meters
  993. dpt.rotation = Projectiles_RateRotation * pi / 180.0f;
  994. dpt.color.r = Projectiles_Red / 100.0f;
  995. dpt.color.g = Projectiles_Green / 100.0f;
  996. dpt.color.b = Projectiles_Blue / 100.0f;
  997. dpt.color.a = Projectiles_Alpha / 100.0f;
  998. dpt.damageType = Projectiles_DamageType;
  999. ;
  1000. dpt.bDirectional = !!Projectiles_IsDirectional;
  1001. dpt.width = Projectiles_WidthOverHeight;
  1002. dpt.ambientSound = Projectiles_AmbientSound;
  1003. IprojectileTypeIGC * pprojtype = (IprojectileTypeIGC *)
  1004. g.trekCore->CreateObject(g.timeNow, OT_projectileType, &dpt, sizeof(dpt));
  1005. pprojtype->Release();
  1006. }
  1007. // Load weapons
  1008. SQL_GO(Weapons);
  1009. while (SQL_SUCCEEDED(SQL_GETROW(Weapons)))
  1010. {
  1011. DataWeaponTypeIGC dwt;
  1012. CopyMemory(dwt.modelName, Weapons_FileModel, sizeof(Weapons_FileModel));
  1013. SET_PARTS(dwt, Weapons)
  1014. dwt.dtimeReady = (float) Weapons_dTimeReady / 1000.0f;
  1015. dwt.dtimeBurst = Weapons_dTimeBurstShots / 1000.0f;
  1016. dwt.energyPerShot = Weapons_EnergyPerShot;
  1017. dwt.dispersion = Weapons_Dispersion;
  1018. dwt.projectileTypeID = Weapons_ProjectileID1;
  1019. dwt.cAmmoPerShot = Weapons_cBulletsPerShot;
  1020. dwt.activateSound = Weapons_ActivateSound;
  1021. dwt.singleShotSound = Weapons_SingleShotSound;
  1022. dwt.burstSound = Weapons_BurstShotSound;
  1023. IweaponIGC * pweapon = (IweaponIGC *) g.trekCore->CreateObject(
  1024. g.timeNow, OT_partType, &dwt, sizeof(dwt));
  1025. pweapon->Release();
  1026. }
  1027. // Load Missiles
  1028. SQL_GO(Missiles);
  1029. while (SQL_SUCCEEDED(SQL_GETROW(Missiles)))
  1030. {
  1031. DataMissileTypeIGC missile;
  1032. SET_EXPEND(missile, Missiles)
  1033. missile.maxLock = Missiles_MaxLock;
  1034. missile.chaffResistance = Missiles_Resistance;
  1035. missile.acceleration = Missiles_Acceleration;
  1036. missile.turnRate = RadiansFromDegrees(Missiles_TurnRate);
  1037. missile.initialSpeed = Missiles_InitialSpeed;
  1038. //missile.armTime = Missiles_ArmTime;
  1039. missile.lockTime = Missiles_LockTime;
  1040. missile.readyTime = Missiles_ReadyTime;
  1041. missile.dispersion = Missiles_Dispersion;
  1042. missile.lockAngle = RadiansFromDegrees(Missiles_LockAngle);
  1043. missile.power = Missiles_Power;
  1044. missile.damageType = Missiles_DamageType;
  1045. missile.blastPower = Missiles_BlastPower;
  1046. missile.blastRadius = Missiles_BlastRadius;
  1047. missile.bDirectional = !!Missiles_IsDirectional;
  1048. missile.width = Missiles_WidthOverHeight;
  1049. missile.launchSound = Missiles_LaunchSound;
  1050. missile.ambientSound = Missiles_FlightSound;
  1051. ImissileIGC * pmissile = (ImissileIGC *) g.trekCore->CreateObject(g.timeNow, OT_missileType,
  1052. &missile, sizeof(missile));
  1053. pmissile->Release();
  1054. }
  1055. // Load Chaff
  1056. SQL_GO(Chaff);
  1057. while (SQL_SUCCEEDED(SQL_GETROW(Chaff)))
  1058. {
  1059. DataChaffTypeIGC chaff;
  1060. SET_EXPEND(chaff, Chaff)
  1061. chaff.chaffStrength = Chaff_Strength;
  1062. IObject* u = g.trekCore->CreateObject(g.timeNow, OT_chaffType,
  1063. &chaff, sizeof(chaff));
  1064. u->Release();
  1065. }
  1066. // Load shields
  1067. SQL_GO(Shields);
  1068. while (SQL_SUCCEEDED(SQL_GETROW(Shields)))
  1069. {
  1070. DataShieldTypeIGC dst;
  1071. SET_PARTS(dst, Shields)
  1072. dst.rateRegen = Shields_RegenRate;
  1073. dst.maxStrength = Shields_MaxHitPoints;
  1074. dst.defenseType = Shields_DefenseType;
  1075. dst.activateSound = Shields_ActivateSound;
  1076. dst.deactivateSound = Shields_DeactivateSound;
  1077. IpartTypeIGC * pparttype = (IpartTypeIGC *) g.trekCore->CreateObject(
  1078. g.timeNow, OT_partType, &dst, sizeof(dst));
  1079. pparttype->Release();
  1080. }
  1081. SQL_GO(Cloaks);
  1082. while (SQL_SUCCEEDED(SQL_GETROW(Cloaks)))
  1083. {
  1084. DataCloakTypeIGC dct;
  1085. SET_PARTS(dct, Cloaks)
  1086. dct.energyConsumption = Cloaks_EnergyConsumption;
  1087. dct.maxCloaking = Cloaks_MaxCloaking;
  1088. dct.onRate = Cloaks_OnRate;
  1089. dct.offRate = Cloaks_OffRate;
  1090. dct.engageSound = Cloaks_EngageSound;
  1091. dct.disengageSound = Cloaks_DisengageSound;
  1092. IpartTypeIGC * pparttype = (IpartTypeIGC *) g.trekCore->CreateObject(
  1093. g.timeNow, OT_partType, &dct, sizeof(dct));
  1094. pparttype->Release();
  1095. }
  1096. // Load afterburners
  1097. SQL_GO(Afterburners);
  1098. while (SQL_SUCCEEDED(SQL_GETROW(Afterburners)))
  1099. {
  1100. DataAfterburnerTypeIGC dab;
  1101. SET_PARTS(dab, Afterburners)
  1102. dab.fuelConsumption = Afterburners_RateBurn;
  1103. dab.maxThrust = Afterburners_ThrustMax;
  1104. dab.onRate = Afterburners_RateOn;
  1105. dab.offRate = Afterburners_RateOff;
  1106. dab.interiorSound = Afterburners_InteriorSound;
  1107. dab.exteriorSound = Afterburners_ExteriorSound;
  1108. IpartTypeIGC * pparttype = (IpartTypeIGC *) g.trekCore->CreateObject(
  1109. g.timeNow, OT_partType, &dab, sizeof(dab));
  1110. pparttype->Release();
  1111. }
  1112. // Load packs
  1113. SQL_GO(Ammo);
  1114. while (SQL_SUCCEEDED(SQL_GETROW(Ammo)))
  1115. {
  1116. DataPackTypeIGC dpt;
  1117. SET_PARTS(dpt, Ammo)
  1118. dpt.amount = Ammo_Qty;
  1119. dpt.packType = Ammo_AmmoType;
  1120. IpartTypeIGC * pparttype = (IpartTypeIGC *) g.trekCore->CreateObject(
  1121. g.timeNow, OT_partType, &dpt, sizeof(dpt));
  1122. pparttype->Release();
  1123. }
  1124. SQL_GO(Mines);
  1125. while (SQL_SUCCEEDED(SQL_GETROW(Mines)))
  1126. {
  1127. DataMineTypeIGC dmt;
  1128. SET_EXPEND(dmt, Mines)
  1129. dmt.endurance = float(Mines_MunitionCount) * Mines_Power;
  1130. dmt.power = Mines_Power;
  1131. dmt.radius = Mines_PlacementRadius;
  1132. dmt.damageType = Mines_DamageType;
  1133. IObject* u = g.trekCore->CreateObject(g.timeNow, OT_mineType,
  1134. &dmt, sizeof(dmt));
  1135. u->Release();
  1136. }
  1137. SQL_GO(Probes);
  1138. while (SQL_SUCCEEDED(SQL_GETROW(Probes)))
  1139. {
  1140. DataProbeTypeIGC dpt;
  1141. SET_EXPEND(dpt, Probes)
  1142. dpt.scannerRange = Probes_ScanRange;
  1143. dpt.projectileTypeID = Probes_ProjectileTypeID;
  1144. dpt.accuracy = float(Probes_accuracy) / 100.0f;
  1145. dpt.dtimeBurst = float(Probes_dtimeBurst) / 1000.0f;
  1146. dpt.dispersion = Probes_dispersion;
  1147. dpt.ammo = Probes_ammo;
  1148. dpt.ambientSound = Probes_AmbientSound;
  1149. dpt.dtRipcord = (Probes_dtRipcord != 255)? float(Probes_dtRipcord) : -1.0f;
  1150. IObject* u = g.trekCore->CreateObject(g.timeNow, OT_probeType,
  1151. &dpt, sizeof(dpt));
  1152. u->Release();
  1153. }
  1154. // Load Magazines
  1155. SQL_GO(Magazines);
  1156. while (SQL_SUCCEEDED(SQL_GETROW(Magazines)))
  1157. {
  1158. DataLauncherTypeIGC magazine;
  1159. //Other fields are not used (data is gotten from the missile type)
  1160. magazine.partID = Magazine_PartID;
  1161. magazine.expendabletypeID = Magazine_ExpendableID;
  1162. magazine.amount = Magazine_Amount;
  1163. magazine.launchCount = Magazine_LaunchCount;
  1164. magazine.successorPartID = Magazine_SuccessorID;
  1165. SQLSTRCPY(magazine.inventoryLineMDL, Magazine_InventoryLineMDL);
  1166. IpartTypeIGC * pparttype = (IpartTypeIGC *) g.trekCore->CreateObject(
  1167. g.timeNow, OT_partType, &magazine, sizeof(magazine));
  1168. pparttype->Release();
  1169. }
  1170. for (i = 0; i < nCount; i++)
  1171. {
  1172. // Gotta get each ship as separate SQL_GO, because can't have nested SQL_GO's, and we get attachpoints for ship below
  1173. ShipTypes_ShipTypeID = vnShipIdList[i];
  1174. SQL_GO(ShipTypes);
  1175. if (SQL_SUCCEEDED(sqlret = SQL_GETROW(ShipTypes)))
  1176. {
  1177. // Not all ship types exist, but we check 'em all anyway
  1178. // First find out how many attachment points this ship has
  1179. AttachPointsCount_ShipTypeID = ShipTypes_ShipTypeID;
  1180. SQL_GO(AttachPointsCount);
  1181. SQL_GETROW(AttachPointsCount);
  1182. CB cbHull = sizeof(DataHullTypeIGC) + g_cAttachPoints * sizeof(HardpointData);
  1183. DataHullTypeIGC * pdht = (DataHullTypeIGC *) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT, cbHull);
  1184. SQLSTRCPY(pdht->modelName, ShipTypes_ShipModelName);
  1185. SQLSTRCPY(pdht->iconName, ShipTypes_ShipIconName);
  1186. SQLSTRCPY(pdht->name, ShipTypes_Name);
  1187. SQLSTRCPY(pdht->description, ShipTypes_Description);
  1188. //SQLSTRCPY(pdht->pilotHUDName, ShipTypes_PilotHUD);
  1189. //SQLSTRCPY(pdht->observerHUDName, ShipTypes_ObserverHUD);
  1190. #define defaultPart(x) pdht->preferredPartsTypes[x-1] = ShipTypes_PreferredPart##x > 0 ? ShipTypes_PreferredPart##x : NA;
  1191. defaultPart(1)
  1192. defaultPart(2)
  1193. defaultPart(3)
  1194. defaultPart(4)
  1195. defaultPart(5)
  1196. defaultPart(6)
  1197. defaultPart(7)
  1198. defaultPart(8)
  1199. defaultPart(9)
  1200. defaultPart(10)
  1201. defaultPart(11)
  1202. defaultPart(12)
  1203. defaultPart(13)
  1204. defaultPart(14)
  1205. #undef defaultPart
  1206. pdht->hardpointOffset = sizeof(DataHullTypeIGC);
  1207. pdht->price = ShipTypes_Price;
  1208. pdht->mass = ShipTypes_flWeight;
  1209. pdht->hullID = ShipTypes_ShipTypeID;
  1210. pdht->length = ShipTypes_flLength;
  1211. pdht->speed = ShipTypes_SpeedMax;
  1212. pdht->maxTurnRates[c_axisYaw] = ShipTypes_flRateYaw * pi / 180.0f;
  1213. pdht->maxTurnRates[c_axisPitch] = ShipTypes_flRatePitch * pi / 180.0f;
  1214. pdht->maxTurnRates[c_axisRoll] = ShipTypes_flRateRoll * pi / 180.0f;
  1215. float fYaw = ShipTypes_flRateYaw * ShipTypes_flRateYaw / (2.0f * ShipTypes_flDriftYaw);
  1216. float fPitch = ShipTypes_flRatePitch * ShipTypes_flRatePitch / (2.0f * ShipTypes_flDriftPitch);
  1217. float fRoll = ShipTypes_flRateRoll * ShipTypes_flRateRoll / (2.0f * ShipTypes_flDriftPitch);
  1218. pdht->turnTorques[c_axisYaw] = pdht->mass * fYaw * pi / 180.0f;
  1219. pdht->turnTorques[c_axisPitch] = pdht->mass * fPitch * pi / 180.0f;
  1220. pdht->turnTorques[c_axisRoll] = pdht->mass * fRoll * pi / 180.0f;
  1221. pdht->thrust = pdht->mass * ShipTypes_flAcceleration;
  1222. pdht->sideMultiplier = ShipTypes_flAccelSideMult;
  1223. pdht->backMultiplier = ShipTypes_flAccelBackMult;
  1224. pdht->signature = (float)ShipTypes_BaseSignature / 100.0f;
  1225. pdht->scannerRange = ShipTypes_RangeScanner;
  1226. pdht->hitPoints = ShipTypes_HitPoints;
  1227. pdht->defenseType = ShipTypes_DefenseType;
  1228. pdht->maxEnergy = ShipTypes_EnergyMax;
  1229. pdht->habmCapabilities = ShipTypes_Capabilities;
  1230. pdht->rechargeRate = ShipTypes_RateRechargeEnergy;
  1231. /*
  1232. assert (ShipTypes_flRateYaw == ShipTypes_flRatePitch);
  1233. assert (ShipTypes_flRateYaw == ShipTypes_flRateRoll);
  1234. assert (ShipTypes_flDriftYaw == ShipTypes_flDriftPitch);
  1235. assert (ShipTypes_flDriftYaw == ShipTypes_flDriftPitch);
  1236. debugf("%s\n", pdht->name);
  1237. debugf("\t%16.3f%16.3f\n", ShipTypes_flRateYaw, fYaw);
  1238. debugf("\t%16.3f%16.3f\n", ShipTypes_flRatePitch, fPitch);
  1239. debugf("\t%16.3f%16.3f\n", ShipTypes_flRateRoll, fRoll);
  1240. */
  1241. pdht->ecm = ShipTypes_ecm;
  1242. pdht->ripcordSpeed = ShipTypes_ripcordSpeed * g.trekCore->GetFloatConstant(c_fcidRipcordTime);
  1243. pdht->ripcordCost = ShipTypes_ripcordCost;
  1244. pdht->maxFuel = float(ShipTypes_MaxFuel);
  1245. pdht->maxAmmo = ShipTypes_MaxAmmo;
  1246. // set all ships to the largest capacity necessary to hold anything, until we get the numbers from the database.
  1247. pdht->pmEquipment[ET_ChaffLauncher] = ShipTypes_ChaffPartMask;
  1248. pdht->pmEquipment[ET_Shield] = ShipTypes_ShieldPartMask;
  1249. pdht->pmEquipment[ET_Afterburner] = ShipTypes_AfterburnerPartMask;
  1250. pdht->pmEquipment[ET_Cloak] = ShipTypes_CloakPartMask;
  1251. pdht->pmEquipment[ET_Magazine] = ShipTypes_MagazinePartMask;
  1252. pdht->pmEquipment[ET_Dispenser] = ShipTypes_DispenserPartMask;
  1253. pdht->capacityMagazine = ShipTypes_MagazineCapacity;
  1254. pdht->capacityDispenser = ShipTypes_DispenserCapacity;
  1255. pdht->capacityChaffLauncher = ShipTypes_ChaffCapacity;
  1256. pdht->successorHullID = ShipTypes_SuccessorID;
  1257. pdht->interiorSound = ShipTypes_InteriorSound;
  1258. pdht->exteriorSound = ShipTypes_ExteriorSound;
  1259. pdht->mainThrusterInteriorSound = ShipTypes_ThrustInteriorSound;
  1260. pdht->mainThrusterExteriorSound = ShipTypes_ThrustExteriorSound;
  1261. pdht->manuveringThrusterInteriorSound = ShipTypes_TurnInteriorSound;
  1262. pdht->manuveringThrusterExteriorSound = ShipTypes_TurnExteriorSound;
  1263. TechsListToBits(ShipTypes_TechBitsReqd, pdht->ttbmRequired);
  1264. TechsListToBits(ShipTypes_TechBitsEffect, pdht->ttbmEffects);
  1265. assert (pdht->ttbmEffects <= pdht->ttbmRequired);
  1266. AttachPoints_ShipTypeID = ShipTypes_ShipTypeID;
  1267. pdht->maxWeapons = 0;
  1268. pdht->maxFixedWeapons = 0;
  1269. pdht->timeToBuild = ShipTypes_TimeToBuild;
  1270. pdht->groupID = ShipTypes_GroupID;
  1271. SQL_GO(AttachPoints);
  1272. {
  1273. HardpointData * phpd = (HardpointData*)((char*)pdht + pdht->hardpointOffset);
  1274. while (SQL_SUCCEEDED(sqlret = SQL_GETROW(AttachPoints)))
  1275. {
  1276. SQLSTRCPY(phpd->frameName, AttachPoints_FrameName);
  1277. //SQLSTRCPY(phpd->hudName, AttachPoints_GunnerHUD);
  1278. SQLSTRCPY(phpd->locationAbreviation, AttachPoints_LocationAbreviation);
  1279. phpd->bFixed = (AttachPoints_PartTypeID == ET_Weapon);
  1280. //Never more than 4 unmanned weapons (and they have to be listed
  1281. //first)
  1282. assert ((!phpd->bFixed) ||
  1283. (( (char*)phpd - ((char*)pdht + pdht->hardpointOffset) ) <
  1284. c_maxUnmannedWeapons * sizeof(HardpointData)));
  1285. /*
  1286. phpd->minDot = (AttachPoints_FieldOfFire < 270)
  1287. ? float(cos(0.5f * RadiansFromDegrees(float(AttachPoints_FieldOfFire))))
  1288. : -1.0f;
  1289. */
  1290. phpd->interiorSound = AttachPoints_InteriorSound;
  1291. phpd->turnSound = AttachPoints_TurnSound;
  1292. phpd->partMask = AttachPoints_PartMask;
  1293. pdht->maxWeapons++;
  1294. if (phpd->bFixed)
  1295. pdht->maxFixedWeapons++;
  1296. phpd++;
  1297. }
  1298. //assert (iAttach == g.cAttachPoints);
  1299. }
  1300. /*
  1301. {
  1302. HullAbilityBitMask habm = pdht->habmCapabilities;
  1303. habm &= ~c_habmRemoteLeadIndicator;
  1304. if (habm != pdht->habmCapabilities)
  1305. {
  1306. debugf("%-20s %8d %8d -> %8d\n", pdht->name, pdht->hullID, pdht->habmCapabilities, habm);
  1307. }
  1308. }
  1309. */
  1310. IhullTypeIGC * phulltype = (IhullTypeIGC *) g.trekCore->CreateObject(g.timeNow, OT_hullType,
  1311. pdht, cbHull);
  1312. phulltype->Release();
  1313. }
  1314. }
  1315. // Load Developments
  1316. // to preserve DB ordering of developments, get the ids in database order
  1317. SQL_GO(DevelopmentsCount);
  1318. SQL_GETROW(DevelopmentsCount);
  1319. DevelopmentID* vnDevelopmentIdList = (DevelopmentID*)_alloca(g_cDevelopments * sizeof(DevelopmentID));
  1320. nCount = 0;
  1321. SQL_GO(DevelopmentsIDs);
  1322. while (SQL_SUCCEEDED(sqlret = SQL_GETROW(DevelopmentsIDs)))
  1323. {
  1324. vnDevelopmentIdList[nCount] = DevelopmentIDs_DevelopmentID;
  1325. ++nCount;
  1326. assert(nCount <= g_cDevelopments);
  1327. }
  1328. for (i = 0; i < nCount; i++)
  1329. {
  1330. // Gotta get each dev as separate SQL_GO, because can't have nested SQL_GO's, and we get other stuff below
  1331. Developments_ID = vnDevelopmentIdList[i];
  1332. SQL_GO(Developments);
  1333. if (SQL_SUCCEEDED(sqlret = SQL_GETROW(Developments)))
  1334. {
  1335. DataDevelopmentIGC dd;
  1336. dd.price = Developments_Price;
  1337. SQLSTRCPY(dd.modelName, Developments_Bitmap);
  1338. SQLSTRCPY(dd.name, Developments_Name);
  1339. SQLSTRCPY(dd.iconName, Developments_IconName);
  1340. SQLSTRCPY(dd.description, Developments_Description);
  1341. TechsListToBits(Developments_TechBitsReqd, dd.ttbmRequired);
  1342. TechsListToBits(Developments_TechBitsEffect, dd.ttbmEffects);
  1343. dd.timeToBuild = Developments_SecondsToBuild;
  1344. dd.completionSound = Developments_CompletedSound;
  1345. dd.groupID = Developments_GroupID;
  1346. assert (dd.timeToBuild > 0);
  1347. dd.developmentID = Developments_ID;
  1348. if (NA == Developments_GlobalAttribute)
  1349. Developments_GlobalAttribute = 0; // IGC wants zero for no global attribute
  1350. FetchGlobalAttributeSet(Developments_GlobalAttribute, dd.gas);
  1351. IdevelopmentIGC * pdev = (IdevelopmentIGC *) g.trekCore->CreateObject(
  1352. g.timeNow, OT_development, &dd, sizeof(dd));
  1353. pdev->Release();
  1354. }
  1355. }
  1356. /*
  1357. // Load effects
  1358. SQL_GO(EffectsCount);
  1359. SQL_GETROW(EffectsCount);
  1360. g.pargEffect = (FMD_S_EFFECT *)
  1361. GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT, g_cEffects * sizeof(g.pargEffect[0]));
  1362. sqlret = SQL_GO(Effects);
  1363. for(i = 0; SQL_SUCCEEDED(sqlret = SQL_GETROW(Effects)); i++)
  1364. {
  1365. FMD_S_EFFECT * pfmEffect = &g.pargEffect[i];
  1366. BEGIN_PFM_CREATE_PREALLOC(g.fm, pfmEffect, S, EFFECT)
  1367. END_PFM_CREATE
  1368. SQLSTRCPY(pfmEffect->szFileSound, Effect_FileSound);
  1369. //SQLSTRCPY(pfmEffect->szDescription, Effect_Description); //NYI not used
  1370. pfmEffect->soundID = Effect_ID;
  1371. pfmEffect->fLooping = !!Effect_IsLooping;
  1372. }
  1373. */
  1374. // Load Drones
  1375. SQL_GO(Drones);
  1376. while (SQL_SUCCEEDED(SQL_GETROW(Drones)))
  1377. {
  1378. DataDroneTypeIGC ddt;
  1379. ddt.price = Drones_Price;
  1380. lstrcpy(ddt.modelName, g.trekCore->GetHullType(Drones_ShipTypeID)->GetModelName());
  1381. ddt.iconName[0] = '\0'; //Icon never seen for drone types
  1382. SQLSTRCPY(ddt.name, Drones_Name);
  1383. SQLSTRCPY(ddt.description, Drones_Description);
  1384. TechsListToBits(Drones_TechBitsReqd, ddt.ttbmRequired);
  1385. TechsListToBits(Drones_TechBitsEffect, ddt.ttbmEffects);
  1386. ddt.timeToBuild = Drones_SecToBuild;
  1387. //assert (ddt.timeToBuild > 0);
  1388. ddt.pilotType = (PilotType)Drones_DroneType;
  1389. ddt.hullTypeID = Drones_ShipTypeID;
  1390. ddt.droneTypeID = Drones_ID;
  1391. ddt.shootSkill = 0.5f; //NYI get from DB
  1392. ddt.moveSkill = 0.5f;
  1393. ddt.bravery = 0.5f;
  1394. ddt.groupID = Drones_GroupID;
  1395. ddt.etidLaid = Drones_ExpendableID;
  1396. IdroneTypeIGC * pdronetype = (IdroneTypeIGC *) g.trekCore->CreateObject(
  1397. g.timeNow, OT_droneType, &ddt, sizeof(ddt));
  1398. pdronetype->Release();
  1399. }
  1400. // Load station types
  1401. SQL_GO(StationTypes);
  1402. while(SQL_SUCCEEDED(SQL_GETROW(StationTypes)))
  1403. {
  1404. DataStationTypeIGC st;
  1405. memset(&st, 0, sizeof(st)); //for boundschecker
  1406. SQLSTRCPY(st.name, Stations_Name);
  1407. SQLSTRCPY(st.description, Stations_Description);
  1408. SQLSTRCPY(st.modelName, Stations_Model);
  1409. SQLSTRCPY(st.iconName, Stations_IconName);
  1410. st.textureName[0] = '\0';
  1411. SQLSTRCPY(st.builderName, Stations_BuilderName);
  1412. TechsListToBits(Stations_TBReqd, st.ttbmRequired);
  1413. TechsListToBits(Stations_TBEffect, st.ttbmEffects);
  1414. TechsListToBits(Stations_TBLocal, st.ttbmLocal);
  1415. st.price = Stations_Price;
  1416. st.signature = float(Stations_Signature) / 100.0f;
  1417. st.maxArmorHitPoints = Stations_HPArmor;
  1418. st.defenseTypeArmor = Stations_DTArmor;
  1419. st.maxShieldHitPoints = Stations_HPShield;
  1420. st.defenseTypeShield = Stations_DTShield;
  1421. st.armorRegeneration = Stations_RateRegenArmor;
  1422. st.shieldRegeneration = Stations_RateRegenShield;
  1423. st.timeToBuild = Stations_SecondsToBuild;
  1424. assert (st.timeToBuild > 0);
  1425. st.radius = Stations_Radius;
  1426. st.income = Stations_Income;
  1427. st.stationTypeID = Stations_ID;
  1428. st.sabmCapabilities = Stations_Capabilities;
  1429. st.scannerRange = Stations_ScanRange;
  1430. st.aabmBuild = Stations_AsteroidDiscountMask;
  1431. st.successorStationTypeID = Stations_UpgradeStationTypeID;
  1432. st.classID = Stations_ClassID;
  1433. st.groupID = Stations_GroupID;
  1434. st.constructionDroneTypeID = Stations_ConstructionDroneTypeID;
  1435. st.constructorNeedRockSound = Stations_BuildLocationSound;
  1436. st.constructorUnderAttackSound = Stations_ConstructorAttackedSound;
  1437. st.constructorDestroyedSound = Stations_ConstructorDestroyedSound;
  1438. st.completionSound = Stations_CompletedSound;
  1439. st.exteriorSound = Stations_ExteriorSound;
  1440. st.interiorSound = Stations_InteriorSound;
  1441. st.interiorAlertSound = Stations_InteriorAlertSound;
  1442. st.underAttackSound = Stations_UnderAttackSound;
  1443. st.criticalSound = Stations_CriticalSound;
  1444. st.destroyedSound = Stations_DestroyedSound;
  1445. st.capturedSound = Stations_CapturedSound;
  1446. st.enemyDestroyedSound = Stations_EnemyDestroyedSound;
  1447. st.enemyCapturedSound = Stations_EnemyCapturedSound;
  1448. /*
  1449. {
  1450. StationAbilityBitMask sabm = st.sabmCapabilities;
  1451. sabm &= ~c_sabmRemoteLeadIndicator;
  1452. if (sabm != st.sabmCapabilities)
  1453. debugf("%-20s %8d %8d -> %8d\n", st.name, st.stationTypeID, st.sabmCapabilities, sabm);
  1454. }
  1455. */
  1456. IObject* punk = g.trekCore->CreateObject(g.timeNow, OT_stationType,
  1457. &st, sizeof(st));
  1458. punk->Release();
  1459. }
  1460. // Load Civs
  1461. SQL_GO(CivsCount);
  1462. SQL_GETROW(CivsCount);
  1463. for (i = 0; i < g_cCivs; i++)
  1464. {
  1465. // Gotta get each dev as separate SQL_GO, because can't have nested SQL_GO's, and we get other stuff below
  1466. Civs_ID = i;
  1467. SQL_GO(Civs);
  1468. if (SQL_SUCCEEDED(sqlret = SQL_GETROW(Civs)))
  1469. {
  1470. DataCivilizationIGC c;
  1471. SQLSTRCPY(c.name, Civs_Name);
  1472. SQLSTRCPY(c.iconName, Civs_IconName);
  1473. SQLSTRCPY(c.hudName, Civs_HUDName);
  1474. c.lifepod = Civs_DefaultShipType;
  1475. c.initialStationTypeID = Civs_InitialStationTypeID;
  1476. c.civilizationID = Civs_ID;
  1477. c.bonusMoney = Civs_BonusMoney;
  1478. c.incomeMoney = Civs_IncomeMoney;
  1479. TechsListToBits(Civs_TechBits, c.ttbmBaseTechs);
  1480. TechsListToBits(Civs_TechBitsNoDev, c.ttbmNoDevTechs);
  1481. FetchGlobalAttributeSet(Civs_GlobalAttr, c.gasBaseAttributes);
  1482. IcivilizationIGC * pciv = (IcivilizationIGC *) g.trekCore->CreateObject(
  1483. g.timeNow, OT_civilization, &c, sizeof(c));
  1484. pciv->Release();
  1485. }
  1486. }
  1487. //Load TreasureChances
  1488. SQL_GO(TreasureSets);
  1489. while (SQL_SUCCEEDED(SQL_GETROW(TreasureSets)))
  1490. {
  1491. DataTreasureSetIGC dts;
  1492. SQLSTRCPY(dts.name, TreasureSets_Name);
  1493. dts.treasureSetID = TreasureSets_ID;
  1494. dts.bZoneOnly = (TreasureSets_IsZoneOnly != 0);
  1495. dts.nTreasureData = 0;
  1496. IbaseIGC* pb = g.trekCore->CreateObject(g.timeNow, OT_treasureSet,
  1497. &dts, sizeof(DataTreasureSetIGC));
  1498. pb->Release();
  1499. }
  1500. assert (g.trekCore->GetTreasureSets()->n() > 0); // we should have at least 1
  1501. SQL_GO(TreasureChance);
  1502. while (SQL_SUCCEEDED(SQL_GETROW(TreasureChance)))
  1503. {
  1504. ItreasureSetIGC* pts = g.trekCore->GetTreasureSet(TreasureChance_SetID);
  1505. assert (pts);
  1506. pts->AddTreasureData(TreasureChance_TreasureCode, TreasureChance_ObjectID, TreasureChance_Chance);
  1507. }
  1508. #pragma warning(default : 4244)
  1509. #endif // !defined(ALLSRV_STANDALONE)
  1510. }
  1511. CFMGroup * GetGroupSectorDocked(IclusterIGC * pCluster)
  1512. {
  1513. ClusterGroups * pcg = ((CFSCluster*) pCluster->GetPrivateData())->GetClusterGroups();
  1514. return pcg->pgrpClusterDocked;
  1515. }
  1516. CFMGroup * GetGroupSectorFlying(IclusterIGC * pCluster)
  1517. {
  1518. ClusterGroups * pcg = ((CFSCluster*) pCluster->GetPrivateData())->GetClusterGroups();
  1519. return pcg->pgrpClusterFlying;
  1520. }
  1521. /*-------------------------------------------------------------------------
  1522. * ExportObj
  1523. *-------------------------------------------------------------------------
  1524. * Purpose:
  1525. * export an igc object to the client
  1526. *
  1527. * Parameters:
  1528. * ppfmExport: If supplied, allocates the message there, to be freed by caller
  1529. *
  1530. * Side Effects:
  1531. * Export message queued up
  1532. */
  1533. void ExportObj(IbaseIGC * pIGC, ObjectType ot, FMD_S_EXPORT ** ppfmExport)
  1534. {
  1535. int cbExport = pIGC->Export(NULL);
  1536. if (ppfmExport)
  1537. {
  1538. BEGIN_PFM_CREATE_ALLOC(g.fm, pfmExport, S, EXPORT)
  1539. FM_VAR_PARM(NULL, cbExport)
  1540. END_PFM_CREATE
  1541. pfmExport->objecttype = ot;
  1542. pIGC->Export(FM_VAR_REF(pfmExport, exportData));
  1543. *ppfmExport = pfmExport;
  1544. }
  1545. else
  1546. {
  1547. BEGIN_PFM_CREATE(g.fm, pfmExport, S, EXPORT)
  1548. FM_VAR_PARM(NULL, cbExport)
  1549. END_PFM_CREATE
  1550. pfmExport->objecttype = ot;
  1551. pIGC->Export(FM_VAR_REF(pfmExport, exportData));
  1552. }
  1553. }
  1554. /*-------------------------------------------------------------------------
  1555. * SendPlayerInfo
  1556. *-------------------------------------------------------------------------
  1557. * Purpose:
  1558. * Tell someone about another player. In this case, the other "player" may be a drone
  1559. *
  1560. * Parameters:
  1561. * pfsPlayer: player GETTING the info, NULL = everyone
  1562. * pfsShip: "player" (human or drone) being identified
  1563. */
  1564. void SendPlayerInfo(CFSPlayer * pfsPlayer, CFSShip * pfsShip, CFSMission* pfsMission, bool bSend)
  1565. {
  1566. const int cCivsMax = 8; // is this specified anywhere else?
  1567. int cCivs = 0;
  1568. PersistPlayerScoreObject rgperplrScore[cCivsMax];
  1569. if (pfsShip->IsPlayer()) // get the whole list
  1570. {
  1571. CFSPlayer * pfsPlayerSent = pfsShip->GetPlayer(); // the player being sent
  1572. PersistPlayerScoreObjectList * pperplrscoList = pfsPlayerSent->GetPersistPlayerScoreObjectList();
  1573. cCivs = pperplrscoList->n();
  1574. assert(cCivsMax >= cCivs);
  1575. PersistPlayerScoreObjectLink * pl = NULL;
  1576. int i = 0;
  1577. for (pl = pperplrscoList->first(); pl; pl = pl->next())
  1578. {
  1579. rgperplrScore[i++] = *pl->data();
  1580. }
  1581. }
  1582. else // just get the one
  1583. {
  1584. cCivs = 1;
  1585. rgperplrScore[0] = pfsShip->GetPlayerScoreObject()->GetPersist();
  1586. }
  1587. BEGIN_PFM_CREATE(g.fm, pfmPlayerInfo, S, PLAYERINFO)
  1588. FM_VAR_PARM(rgperplrScore, cCivs * sizeof(rgperplrScore[0]))
  1589. END_PFM_CREATE
  1590. lstrcpy(pfmPlayerInfo->CharacterName, pfsShip->GetName());
  1591. pfmPlayerInfo->fExperience = pfsShip->GetIGCShip()->GetExperience();
  1592. pfmPlayerInfo->nKills = pfsShip->GetIGCShip()->GetKills();
  1593. pfmPlayerInfo->nDeaths = pfsShip->GetIGCShip()->GetDeaths();
  1594. pfmPlayerInfo->nEjections = pfsShip->GetIGCShip()->GetEjections();
  1595. pfmPlayerInfo->dtidDrone = pfsShip->IsPlayer()
  1596. ? NA
  1597. : ((CFSDrone*)pfsShip)->GetDroneTypeID();
  1598. pfmPlayerInfo->pilotType = pfsShip->GetIGCShip()->GetPilotType();
  1599. pfmPlayerInfo->abmOrders = pfsShip->GetIGCShip()->GetOrdersABM();
  1600. {
  1601. IbaseIGC* pbase = pfsShip->GetIGCShip()->GetBaseData();
  1602. pfmPlayerInfo->baseObjectID = pbase
  1603. ? pbase->GetObjectID() : NA;
  1604. }
  1605. IsideIGC* pside = pfsShip->GetIGCShip()->GetSide();
  1606. if (pside)
  1607. {
  1608. assert (pfsMission);
  1609. pfmPlayerInfo->iSide = pside->GetObjectID();
  1610. }
  1611. else
  1612. {
  1613. //No side means no mission as far as the client can tell
  1614. pfmPlayerInfo->iSide = NA;
  1615. }
  1616. pfmPlayerInfo->shipID = pfsShip->GetShipID();
  1617. //pfmPlayerInfo->wingID = pfsShip->GetIGCShip()->GetWingID();
  1618. pfmPlayerInfo->rankID = 0; //NYI
  1619. pfmPlayerInfo->fReady = pfsShip->IsPlayer() ? pfsShip->GetPlayer()->GetReady() : true; // drones always ready
  1620. pfmPlayerInfo->money = 0; //Do not tell them how much money the player has ...
  1621. if (pfsShip->IsPlayer() && pfsMission && pside && pfmPlayerInfo->iSide != SIDE_TEAMLOBBY)
  1622. {
  1623. CFSPlayer* pfsPlayerNew = pfsShip->GetPlayer();
  1624. pfmPlayerInfo->fTeamLeader = pfsMission->GetLeader(pfmPlayerInfo->iSide) == pfsPlayerNew;
  1625. pfmPlayerInfo->fMissionOwner = pfsMission->GetOwner() == pfsPlayerNew;
  1626. }
  1627. else
  1628. {
  1629. pfmPlayerInfo->fTeamLeader = false;
  1630. pfmPlayerInfo->fMissionOwner = false;
  1631. }
  1632. if (bSend)
  1633. {
  1634. CFMRecipient* prcp;
  1635. if (pfsPlayer)
  1636. prcp = pfsPlayer->GetConnection();
  1637. else
  1638. prcp = pfsMission->GetGroupMission();
  1639. g.fm.SendMessages(prcp, FM_GUARANTEED, FM_FLUSH);
  1640. debugf("Sending PlayerInfo for %s to %s(%u)\n", pfsShip->GetName(),
  1641. prcp->GetName(), prcp->GetID());
  1642. }
  1643. }
  1644. /*
  1645. void CheatSides(CFSShip * pfsShip, CFSMission * pfsMission)
  1646. {
  1647. {
  1648. static _CrtMemState* pstate = NULL;
  1649. static _CrtMemState state;
  1650. _CrtMemDumpAllObjectsSince(pstate);
  1651. _CrtMemCheckpoint(&state);
  1652. pstate = &state;
  1653. }
  1654. // show status on all sides
  1655. SideID iSide;
  1656. debugf("\n");
  1657. const SideListIGC * plistSide = pfsMission->GetIGCMission()->GetSides();
  1658. for (SideLinkIGC * plinkSide = plistSide->first; plinkSide; plinkSide = plinkSide->next())
  1659. {
  1660. IsideIGC * pside = plinkSide->data();
  1661. iSide = pside->GetObjectID();
  1662. int cMembers = pside->GetShips()->n();;
  1663. if (pfsShip)
  1664. SendChatMessage(NULL, CHAT_INDIVIDUAL, pfsShip, c_fCheatsOff,
  1665. "Side %d-%s has %d stations and %d ships:", iSide,
  1666. g.rgMissions[g.missionID].rgszName[iSide],
  1667. g.trekCore->GetSide(iSide)->GetStations()->n(), cMembers);
  1668. debugf("Side %d-%s has %d stations and %d players:\n", iSide,
  1669. g.rgMissions[g.missionID].rgszName[iSide],
  1670. g.trekCore->GetSide(iSide)->GetStations()->n(), cMembers);
  1671. ShipID * rgShipIDMembers = g.rgpargShipIDMembers[g.missionID][iSide];
  1672. int iMember;
  1673. for (iMember = cMembers - 1; iMember >= 0; iMember--)
  1674. {
  1675. CFSShip * pfsshipT = CFSShip::GetShipFromID(rgShipIDMembers[iMember]);
  1676. IshipIGC * pship = pfsshipT->GetIGCShip();
  1677. IclusterIGC * pcluster = pfsshipT->GetCluster();
  1678. IstationIGC * pstation = pfsshipT->GetStation();
  1679. if (!pstation)
  1680. {
  1681. assert (pship);
  1682. if (pcluster) // they're flying
  1683. {
  1684. Vector vPosition = pship->GetPosition();
  1685. if (pfsShip)
  1686. SendChatMessage(NULL, CHAT_INDIVIDUAL, pfsShip, c_fCheatsOff,
  1687. "%s has %d HP, and is flying a %s at %d.%d.%d in sector %s",
  1688. pship->GetName(), (int) (100.0f * pship->GetFraction()),
  1689. pship->GetHullType()->GetName(), (int) vPosition.X(),
  1690. (int) vPosition.Y(), (int) vPosition.Z(), pcluster->GetName());
  1691. debugf(" %s has %d HP, and is flying a %s at %d.%d.%d in sector %s\n",
  1692. pship->GetName(), (int) (100.0f * pship->GetFraction()),
  1693. pship->GetHullType()->GetName(), (int) vPosition.X(),
  1694. (int) vPosition.Y(), (int) vPosition.Z(), pcluster->GetName());
  1695. }
  1696. else // something weird--need to find out why this happens
  1697. {
  1698. if (pfsShip)
  1699. SendChatMessage(NULL, CHAT_INDIVIDUAL, pfsShip, c_fCheatsOff,
  1700. "%s has never left team room", pfsshipT->GetName());
  1701. debugf(" %s has never left team room\n", pfsshipT->GetName());
  1702. }
  1703. }
  1704. else // docked
  1705. {
  1706. assert (pcluster);
  1707. if (pfsShip)
  1708. SendChatMessage(NULL, CHAT_INDIVIDUAL, pfsShip, c_fCheatsOff,
  1709. " %s is docked at station %s in sector %s",
  1710. pfsshipT->GetName(), pstation->GetName(), pcluster->GetName());
  1711. debugf(" %s is docked at station %s in sector %s\n",
  1712. pfsshipT->GetName(), pstation->GetName(), pcluster->GetName());
  1713. }
  1714. }
  1715. }
  1716. }
  1717. */
  1718. CFSDrone * CreateStockDrone(IdroneTypeIGC * pdronetype, IsideIGC* pSide, IbaseIGC* pbase)
  1719. {
  1720. PilotType pt = pdronetype->GetPilotType();
  1721. ImissionIGC* pmission = pSide->GetMission();
  1722. IshipIGC* pship = CreateDrone(pmission,
  1723. -2,
  1724. pt,
  1725. NULL,
  1726. pdronetype->GetHullTypeID(),
  1727. pSide,
  1728. (pt == c_ptMiner) ? c_aabmMineHe3 : 0,
  1729. pdronetype->GetShootSkill(),
  1730. pdronetype->GetMoveSkill(),
  1731. pdronetype->GetBravery());
  1732. assert (pship);
  1733. if (pbase)
  1734. pship->SetBaseData(pbase);
  1735. pship->CreateDamageTrack();
  1736. CFSDrone * pfsDrone = new CFSDrone(pship);
  1737. pfsDrone->SetDroneTypeID(pdronetype->GetObjectID());
  1738. {
  1739. char bfr[c_cbName + 10];
  1740. const char* pszName;
  1741. if (pt == c_ptBuilder)
  1742. {
  1743. assert (pbase);
  1744. assert (pbase->GetObjectType() == OT_stationType);
  1745. pszName = ((IstationTypeIGC*)pbase)->GetBuilderName();
  1746. }
  1747. else
  1748. {
  1749. pszName = pdronetype->GetName();
  1750. }
  1751. assert (strlen(pszName) < c_cbName - 4);
  1752. strcpy(bfr, pszName);
  1753. _itoa(pship->GetObjectID(), bfr + strlen(bfr), 10);
  1754. pship->SetName(bfr);
  1755. }
  1756. pfsDrone->SetDroneTypeID(pdronetype->GetObjectID());
  1757. // Tell everyone about the new player
  1758. SendPlayerInfo(NULL, pfsDrone, (CFSMission *)(pmission->GetPrivateData()));
  1759. pfsDrone->ShipStatusStart(NULL);
  1760. pfsDrone->SetLastDamageReport(pship->GetLastUpdate());
  1761. return pfsDrone;
  1762. }
  1763. bool LeaveShip(CFSShip* pfsShip, IshipIGC* pshipParent)
  1764. {
  1765. assert (pfsShip);
  1766. assert (pfsShip->IsPlayer());
  1767. assert (pshipParent);
  1768. bool bLeft = false;
  1769. if (pshipParent->GetCluster() == NULL)
  1770. {
  1771. IshipIGC* pship = pfsShip->GetIGCShip();
  1772. assert (pship != pshipParent);
  1773. assert (pship->GetParentShip() == pshipParent);
  1774. assert (pship->GetCluster() == NULL);
  1775. CFSShip* pfsShipParent = (CFSShip*)(pshipParent->GetPrivateData());
  1776. assert (pfsShipParent->IsPlayer());
  1777. pship->SetParentShip(NULL);
  1778. bLeft = true;
  1779. IsideIGC* pside = pship->GetSide();
  1780. IhullTypeIGC* pht = pside->GetCivilization()->GetLifepod();
  1781. pship->SetBaseHullType(pht);
  1782. ShipStatus* pss = pfsShip->GetShipStatus(pside->GetObjectID());
  1783. pss->SetState(c_ssDocked);
  1784. pss->SetParentID(NA);
  1785. pss->SetHullID(pht->GetObjectID());
  1786. assert (pship->GetStation());
  1787. assert (pss->GetStationID() == pship->GetStation()->GetObjectID());
  1788. BEGIN_PFM_CREATE(g.fm, pfmLeave, S, LEAVE_SHIP)
  1789. END_PFM_CREATE
  1790. pfmLeave->sidChild = pship->GetObjectID();
  1791. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  1792. }
  1793. return bLeft;
  1794. }
  1795. bool BoardShip(CFSShip* pfsShip, IshipIGC* pshipBoard)
  1796. {
  1797. assert (pfsShip);
  1798. assert (pfsShip->IsPlayer());
  1799. assert (pshipBoard);
  1800. bool bBoarded = false;
  1801. IshipIGC* pship = pfsShip->GetIGCShip();
  1802. if (pship != pshipBoard)
  1803. {
  1804. IhullTypeIGC* phtBoard = pshipBoard->GetBaseHullType();
  1805. if (phtBoard)
  1806. {
  1807. IshipIGC* pshipOldParent = pship->GetParentShip();
  1808. CFSShip* pfsShipBoard = (CFSShip*)(pshipBoard->GetPrivateData());
  1809. IstationIGC* pstationBoard = pshipBoard->GetStation();
  1810. if (pfsShipBoard->IsPlayer() &&
  1811. ((pshipBoard == pshipOldParent) || //Moving within the same ship
  1812. ((pship->GetCluster() == NULL) && //Boarding a ship from elsewhere
  1813. (pship->GetStation() != NULL) &&
  1814. (pshipBoard->GetCluster() == NULL) &&
  1815. (pstationBoard != NULL))))
  1816. {
  1817. Mount turret;
  1818. {
  1819. Mount minMount = phtBoard->GetMaxFixedWeapons();
  1820. Mount maxMount = phtBoard->GetMaxWeapons();
  1821. if (minMount == maxMount)
  1822. turret = NA;
  1823. else
  1824. {
  1825. Mount startMount = pship->GetTurretID();
  1826. if (startMount == NA)
  1827. startMount = maxMount - 1;
  1828. else
  1829. assert (pship->GetParentShip() != NULL);
  1830. turret = startMount;
  1831. while (true)
  1832. {
  1833. turret++;
  1834. if (turret >= maxMount)
  1835. turret = minMount;
  1836. //Is this position occupied?
  1837. ShipLinkIGC* psl;
  1838. for (psl = pshipBoard->GetChildShips()->first();
  1839. (psl != NULL);
  1840. psl = psl->next())
  1841. {
  1842. if (psl->data()->GetTurretID() == turret)
  1843. break;
  1844. }
  1845. if (psl == NULL)
  1846. {
  1847. //Not occupied
  1848. break;
  1849. }
  1850. if (turret == startMount)
  1851. {
  1852. //No available slots ... become an observer
  1853. turret = NA;
  1854. break;
  1855. }
  1856. }
  1857. }
  1858. }
  1859. //Prevent anyone from becoming an observer unless they can cheat
  1860. if ((turret != NA) || pfsShip->GetPlayer()->CanCheat())
  1861. {
  1862. {
  1863. //Anyone onboard pship now needs to fend for themselves
  1864. const ShipListIGC* pchildren = pship->GetChildShips();
  1865. //If we have kids and we are boarding another ship, we must be at a station
  1866. assert ((pchildren->n() == 0) || (pship->GetCluster() == NULL));
  1867. ShipLinkIGC* psl;
  1868. while (psl = pchildren->first()) //Intentional ==
  1869. {
  1870. LeaveShip((CFSShip*)(psl->data()->GetPrivateData()), pship);
  1871. }
  1872. }
  1873. IsideIGC* pside = pship->GetSide();
  1874. {
  1875. //The player is about to lose some expensive hardware ... credit them for it
  1876. Money refund = pship->GetValue();
  1877. assert(refund >= 0);
  1878. if (refund > 0)
  1879. {
  1880. pfsShip->SetMoney(pfsShip->GetMoney() + refund);
  1881. BEGIN_PFM_CREATE(g.fm, pfmMoney, S, MONEY_CHANGE)
  1882. END_PFM_CREATE
  1883. pfmMoney->sidTo = pfsShip->GetShipID();
  1884. pfmMoney->sidFrom = NA;
  1885. pfmMoney->dMoney = refund;
  1886. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  1887. }
  1888. }
  1889. pship->SetParentShip(pshipBoard);
  1890. pship->SetTurretID(turret);
  1891. bBoarded = true;
  1892. ShipStatus* pss = pfsShip->GetShipStatus(pside->GetObjectID());
  1893. pss->SetState(turret == NA ? c_ssObserver : c_ssTurret);
  1894. pss->SetParentID(pshipBoard->GetObjectID());
  1895. pss->SetHullID(NA);
  1896. if (pstationBoard)
  1897. pship->SetStation(pstationBoard);
  1898. pfsShipBoard->QueueLoadoutChange();
  1899. IclusterIGC* pcluster = pship->GetCluster();
  1900. if (pcluster)
  1901. {
  1902. assert (pshipOldParent == pshipBoard);
  1903. g.fm.SendMessages(GetGroupSectorFlying(pcluster), FM_GUARANTEED, FM_FLUSH);
  1904. }
  1905. else
  1906. {
  1907. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  1908. }
  1909. }
  1910. }
  1911. }
  1912. }
  1913. return bBoarded;
  1914. }
  1915. /*-------------------------------------------------------------------------
  1916. * CheatCreateDrones
  1917. *-------------------------------------------------------------------------
  1918. * Purpose:
  1919. * handles cheat code to create drones
  1920. *
  1921. * Parameters:
  1922. * ship issuing cheat, cheat string
  1923. *
  1924. * Returns:
  1925. * whether cheat string could be successfully evaluated
  1926. */
  1927. bool CheatCreateDrones(CFSShip * pfsShip, ZString & strCheat)
  1928. {
  1929. IclusterIGC* pcluster = pfsShip->GetIGCShip()->GetCluster();
  1930. if (!pcluster)
  1931. return false;
  1932. CFSMission * pfsMission = pfsShip->GetMission();
  1933. // Syntax: CreateDrones [DroneType] [[# of drones] [DroneSide] [shipTypeID]]
  1934. bool fIsCheat = true;
  1935. ZString strDroneType(strCheat.GetToken());
  1936. ZString strNumDrones(strCheat.GetToken());
  1937. ZString strDroneSide(strCheat.GetToken());
  1938. ZString strMessage(strCheat.GetToken());
  1939. ZString strShipTypeID(strCheat.GetToken());
  1940. if (strDroneType.IsEmpty())
  1941. fIsCheat = false;
  1942. else
  1943. {
  1944. short iBucket = 0;
  1945. IhullTypeIGC * phull = NULL;
  1946. PilotType ptPilotType;
  1947. IdroneTypeIGC * pdronetype = NULL;
  1948. IsideIGC* psideDrone;
  1949. int iNumDrones;
  1950. if (!lstrcmpi(strDroneType, "carrier"))
  1951. ptPilotType = c_ptCarrier;
  1952. else if (!lstrcmpi(strDroneType, "miner"))
  1953. ptPilotType = c_ptMiner;
  1954. else if (!lstrcmpi(strDroneType, "wingman"))
  1955. ptPilotType = c_ptWingman;
  1956. else
  1957. {
  1958. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  1959. NA, "%s is not a valid drone type. Aborting cheat code.", (PCC) strDroneType);
  1960. fIsCheat = false;
  1961. }
  1962. if (fIsCheat)
  1963. {
  1964. // We have to look through the drone types and look for one of the correct type
  1965. // If there's more than one, too bad, you get the first one we find
  1966. const DroneTypeListIGC * pdronelist = pfsMission->GetIGCMission()->GetDroneTypes();
  1967. DroneTypeLinkIGC * pdronelink;
  1968. for (pdronelink = pdronelist->first(); pdronelink; pdronelink = pdronelink->next())
  1969. {
  1970. pdronetype = pdronelink->data();
  1971. if (pdronetype->GetPilotType() == ptPilotType)
  1972. break;
  1973. }
  1974. // If we couldn't find one, they don't get a drone?
  1975. if (!pdronetype)
  1976. {
  1977. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  1978. NA, "There are not any %s type drones defined. Aborting cheat code.", (const char*)strDroneType);
  1979. fIsCheat = false;
  1980. }
  1981. }
  1982. if (fIsCheat)
  1983. {
  1984. iNumDrones = strNumDrones.IsEmpty() ? 1 : atoi(strNumDrones);
  1985. if (!strDroneSide.IsEmpty())
  1986. {
  1987. psideDrone = pfsMission->GetIGCMission()->GetSide(atoi(strDroneSide));
  1988. if (!psideDrone)
  1989. fIsCheat = false;
  1990. }
  1991. else
  1992. {
  1993. psideDrone = pfsShip->GetSide();
  1994. }
  1995. }
  1996. if (fIsCheat)
  1997. {
  1998. {
  1999. Vector p = Vector::GetZero();
  2000. if (!pfsShip->GetStation()) // only if I am undocked... otherwise make them aroun 0,0,0
  2001. p = pfsShip->GetIGCShip()->GetSourceShip()->GetPosition();
  2002. for (int i=0; i < iNumDrones; i++)
  2003. {
  2004. p.x += random(-200.0f, 200.0f);
  2005. p.y += random(-200.0f, 200.0f);
  2006. p.z += random(-200.0f, 200.0f);
  2007. CFSDrone* pdrone = CreateStockDrone(pdronetype, psideDrone);
  2008. assert (pdrone);
  2009. pdrone->GetIGCShip()->SetAmmo(0x7fff);
  2010. pdrone->GetIGCShip()->SetPosition(p);
  2011. pdrone->GetIGCShip()->SetCluster(pcluster);
  2012. pdrone->ShipStatusLaunched();
  2013. }
  2014. }
  2015. }
  2016. }
  2017. return fIsCheat;
  2018. }
  2019. #ifdef MOVE_TO_FEDMESSAGING
  2020. BOOL FAR PASCAL EnumPlayersCallback(DPID dpId, DWORD dwPlayerType,
  2021. LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext)
  2022. {
  2023. DataEnumPlayers * pdep = (DataEnumPlayers *) lpContext;
  2024. CFSMission * pfsMission = pdep->pfsShip->GetMission();
  2025. char rgchdpn[256]; // buffer for DPNAME
  2026. char szIndent[15];
  2027. assert (pdep->indent < sizeof(szIndent));
  2028. szIndent[pdep->indent] = '\0';
  2029. int i = pdep->indent;
  2030. while (i--)
  2031. szIndent[i] = ' ';
  2032. if (DPPLAYERTYPE_GROUP == dwPlayerType)
  2033. {
  2034. DPNAME * pdpn = (DPNAME *) rgchdpn;
  2035. DWORD dwSize = sizeof(rgchdpn);
  2036. ZSucceeded(g.fm.GetDPlay()->GetGroupName(dpId, rgchdpn, &dwSize));
  2037. if (pdep->pfsShip)
  2038. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pdep->pfsShip->GetIGCShip()->GetObjectID(),
  2039. NA, "%s%s%s (%u): ",
  2040. szIndent, pdpn->lpszShortNameA, pdpn->lpszLongNameA, dpId);
  2041. debugf("%s%s%s (%u): ", szIndent, pdpn->lpszShortNameA, pdpn->lpszLongNameA, dpId);
  2042. DataEnumPlayers depNew = *pdep;
  2043. depNew.indent += 2;
  2044. // First enumerate players
  2045. ZSucceeded(g.fm.GetDPlay()->EnumGroupPlayers( dpId, NULL, EnumPlayersCallback, (LPVOID) &depNew, DPENUMPLAYERS_ALL));
  2046. debugf("\n");
  2047. // Then enumerate child groups
  2048. ZSucceeded(g.fm.GetDPlay()->EnumGroupsInGroup(dpId, NULL, EnumPlayersCallback, (LPVOID) &depNew, DPENUMGROUPS_LOCAL));
  2049. }
  2050. else
  2051. {
  2052. CFSShip * pfsShip = NULL;
  2053. DWORD dwSize = sizeof(CFSShip);
  2054. ZSucceeded(g.fm.GetDPlay()->GetPlayerData(dpId, &pfsShip, &dwSize, DPGET_LOCAL));
  2055. if (pdep->pfsShip)
  2056. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pdep->pfsShip->GetIGCShip()->GetObjectID(), NA, "%s%s", szIndent, pfsShip->GetName());
  2057. debugf("%s, ", pfsShip->GetName());
  2058. }
  2059. return TRUE;
  2060. }
  2061. /*-------------------------------------------------------------------------
  2062. * CheatDPGroups
  2063. *-------------------------------------------------------------------------
  2064. * Purpose:
  2065. * Cheat code to enumerate all our dpgroups. For debugging purposes only
  2066. *
  2067. * Parameters:
  2068. * player requesting
  2069. */
  2070. void CheatDPGroups(CFSShip * pfsShip)
  2071. {
  2072. DataEnumPlayers dep;
  2073. dep.pfsShip = pfsShip;
  2074. dep.indent = 0;
  2075. g.fm.GetDPlay()->EnumGroups(NULL, EnumPlayersCallback, &dep, DPENUMGROUPS_LOCAL);
  2076. if (pfsShip && pfsShip->IsPlayer())
  2077. {
  2078. CFSPlayer * pfsPlayer = pfsShip->GetPlayer();
  2079. char rgchdpn[128];
  2080. DPNAME * pdpn = (DPNAME *) rgchdpn;
  2081. DWORD dwSize = sizeof(rgchdpn);
  2082. DPID dpidGroup = pfsPlayer->GetGroup();
  2083. ZSucceeded(g.fm.GetDPlay()->GetGroupName(dpidGroup, rgchdpn, &dwSize));
  2084. pfsShip->GetMission()->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2085. NA, "You're in group %s%s (%u)", pdpn->lpszShortNameA, pdpn->lpszLongNameA, dpidGroup);
  2086. }
  2087. }
  2088. #endif // MOVE_TO_FEDMESSAGING
  2089. /*-------------------------------------------------------------------------
  2090. * CheatJPD
  2091. *-------------------------------------------------------------------------
  2092. * Purpose:
  2093. * The Joel P Dehlin cheat to create a friendly and enemy ship in front of you
  2094. * First find a wingman drone type. We have to look through the drone
  2095. * types and look for one of the correct type. If there's more than one,
  2096. * too bad, you get the first one we find.
  2097. */
  2098. void CheatJPD(CFSShip * pfsShip)
  2099. {
  2100. IclusterIGC* pcluster = pfsShip->GetIGCShip()->GetCluster();
  2101. if (!pcluster) // do nothing if you try this while docked.
  2102. return;
  2103. IdroneTypeIGC * pdronetype = NULL;
  2104. CFSMission * pfsMission = pfsShip->GetMission();
  2105. const DroneTypeListIGC * pdronelist = pfsMission->GetIGCMission()->GetDroneTypes();
  2106. DroneTypeLinkIGC * pdronelink;
  2107. for (pdronelink = pdronelist->first(); pdronelink && !pdronetype; pdronelink = pdronelink->next())
  2108. {
  2109. IdroneTypeIGC * pdronetypeT = pdronelink->data();
  2110. if (pdronetypeT->GetPilotType() == c_ptWingman)
  2111. pdronetype = pdronetypeT;
  2112. }
  2113. // If we couldn't find one, no cheat?
  2114. if (!pdronetype)
  2115. {
  2116. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2117. NA, "There are not any wingman type drones defined. Aborting cheat code.");
  2118. return;
  2119. }
  2120. IshipIGC * pship = pfsShip->GetIGCShip();
  2121. const Orientation& o = pship->GetOrientation();
  2122. const float flDistanceFromCenter = 200.0f;
  2123. const float flDistanceFromShip = 600.0f;
  2124. Vector p = pship->GetPosition() - o.GetBackward() * flDistanceFromShip +
  2125. o.GetRight() * flDistanceFromCenter;
  2126. // Here's the friendly
  2127. CFSDrone * pfsDrone = CreateStockDrone(pdronetype, pship->GetSide());
  2128. pfsDrone->GetIGCShip()->SetAmmo(0x7fff);
  2129. pfsDrone->GetIGCShip()->SetPosition(p);
  2130. pfsDrone->GetIGCShip()->SetCluster(pship->GetCluster());
  2131. pfsDrone->ShipStatusLaunched();
  2132. // Now we need to find an enemy side
  2133. const SideListIGC * plistSide = pfsMission->GetIGCMission()->GetSides();
  2134. IsideIGC * psideEnemy = NULL;
  2135. IsideIGC * psideMe = pship->GetSide();
  2136. for (SideLinkIGC * plinkSide = plistSide->first();
  2137. plinkSide && NULL == psideEnemy;
  2138. plinkSide = plinkSide->next())
  2139. {
  2140. IsideIGC * psideT = plinkSide->data();
  2141. if (psideT != psideMe) // we found a side
  2142. psideEnemy = psideT;
  2143. }
  2144. if (psideEnemy)
  2145. {
  2146. p += o.GetRight() * (-2 * flDistanceFromCenter); // go back mirrored across the forward verctor
  2147. pfsDrone = CreateStockDrone(pdronetype, psideEnemy);
  2148. pfsDrone->GetIGCShip()->SetAmmo(0x7fff);
  2149. pfsDrone->GetIGCShip()->SetPosition(p);
  2150. pfsDrone->GetIGCShip()->SetCluster(pship->GetCluster());
  2151. pfsDrone->ShipStatusLaunched();
  2152. }
  2153. else
  2154. {
  2155. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2156. NA, "Cannot create enemy ship because an enemy side does not exist.");
  2157. }
  2158. }
  2159. void LogTrapHack(AGCEventID idEvent, CFSPlayer* pfsPlayer,
  2160. CFMConnection* pcnxnFrom, const char* pExpr, UINT nLine)
  2161. {
  2162. // Determine the event parameters
  2163. LPCSTR pszContext = NULL;
  2164. const char* pszSubject = NULL;
  2165. AGCUniqueID idSource = -1;
  2166. if (pfsPlayer)
  2167. {
  2168. pszContext = pfsPlayer->GetIGCShip() ?
  2169. pfsPlayer->GetIGCShip()->GetMission()->GetContextName() : NULL;
  2170. pszSubject = pfsPlayer->GetName();
  2171. idSource =
  2172. static_cast<USHORT>(CAdminUser::DetermineID(pfsPlayer))
  2173. | static_cast<DWORD>(AGC_AdminUser << 16);
  2174. }
  2175. // Perform special processing for each event type
  2176. switch (idEvent)
  2177. {
  2178. case EventID_HackLog:
  2179. assert(pfsPlayer);
  2180. assert(pszSubject);
  2181. assert(pcnxnFrom);
  2182. retailf("HackLog %s: %s %d\n", pszSubject, pExpr, nLine);
  2183. break;
  2184. case EventID_HackBoot:
  2185. assert(pfsPlayer);
  2186. assert(pszSubject);
  2187. assert(pcnxnFrom);
  2188. retailf("HackBoot %s: %s %d\n", pszSubject, pExpr, nLine);
  2189. g.fm.DeleteConnection(*pfsPlayer->GetConnection());
  2190. break;
  2191. case EventID_HackBootNoPlayer:
  2192. assert(!pfsPlayer);
  2193. assert(!pszSubject);
  2194. assert(pcnxnFrom);
  2195. retailf("HackBootNoPlayer %s %d\n", pExpr, nLine);
  2196. g.fm.DeleteConnection(*pcnxnFrom);
  2197. break;
  2198. default:
  2199. ZError("Unexpected AGCEventID passed to LogTrapHack.");
  2200. }
  2201. // Trigger the event
  2202. _AGCModule.TriggerContextEvent(NULL, idEvent, pszContext,
  2203. pszSubject, idSource, -1, -1, 2,
  2204. "Expr", VT_LPSTR, pExpr,
  2205. "Line", VT_I4 , nLine);
  2206. }
  2207. /*-------------------------------------------------------------------------
  2208. * DoCheatCode
  2209. *-------------------------------------------------------------------------
  2210. * Purpose:
  2211. * Parse chat line, and do something
  2212. *
  2213. * Parameters:
  2214. * pfsChip: The ship sending the cheat
  2215. * szMessage: The cheat
  2216. *
  2217. * Returns:
  2218. * Whether message was indeed a cheat, and hence doesn't need to be sent as chat
  2219. */
  2220. bool DoCheatCode(CFSShip * pfsShip, const char * szMessage)
  2221. {
  2222. CFSMission * pfsMission = pfsShip->GetMission();
  2223. bool fIsCheat = true;
  2224. ZString strCheat(szMessage);
  2225. ZString strToken = strCheat.GetToken();
  2226. // debugf("!!! Cheat from %s: %s\n", pfsShip->GetName(), (PCC) strToken);
  2227. if (!lstrcmpi(strToken, "bootme"))
  2228. {
  2229. Sleep(1000); // give the client a chance to get some messages into the receive queue--if they're flying that is
  2230. LogTrapHack(EventID_HackBoot, pfsShip->GetPlayer(), pfsShip->GetPlayer()->GetConnection(), "bootme cheat code", __LINE__);
  2231. }
  2232. else if (!lstrcmpi(strToken, "spew"))
  2233. {
  2234. debugf("===> %s\n", (PCC) strCheat);
  2235. }
  2236. else if (!lstrcmpi(strToken, "stations"))
  2237. {
  2238. // show status on all stations
  2239. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2240. NA, "Station ID: Name, Side (yours is %d), HP, Sector", pfsShip->GetSide()->GetObjectID());
  2241. const StationListIGC * pstations = pfsMission->GetIGCMission()->GetStations();
  2242. StationLinkIGC * pstationLink = NULL;
  2243. for (pstationLink = pstations->first(); pstationLink; pstationLink = pstationLink->next())
  2244. {
  2245. IstationIGC * pStation = pstationLink->data();
  2246. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2247. NA, "%d: %s%s, %d-%s, %d%%/%d%%HP, in %s",
  2248. pStation->GetObjectID(), pStation->GetName(), (pStation->GetStationType()->GetCapabilities() & c_sabmFlag) ? " (flag)" : "",
  2249. pStation->GetSide()->GetObjectID(), pStation->GetSide()->GetName(),
  2250. (int)(100.0f * pStation->GetFraction()), (int)(100.0f * pStation->GetShieldFraction()), pStation->GetCluster()->GetName());
  2251. }
  2252. }
  2253. else if (!lstrcmpi(strToken, "exile"))
  2254. {
  2255. ZString strPlayer(strCheat.GetToken());
  2256. if (strPlayer.IsEmpty()) // must specify valid station
  2257. fIsCheat = false;
  2258. else
  2259. {
  2260. //Find a player with the given name
  2261. for (ShipLinkIGC* psl = pfsMission->GetIGCMission()->GetShips()->first(); (psl != NULL); psl = psl->next())
  2262. {
  2263. CFSShip* pfsShip = (CFSShip*)(psl->data()->GetPrivateData());
  2264. if (pfsShip->IsPlayer() && (lstrcmpi(strPlayer, psl->data()->GetName()) == 0))
  2265. {
  2266. // send them to the game lobby
  2267. pfsMission->RemovePlayerFromMission(pfsShip->GetPlayer(), QSR_AdminBooted);
  2268. break;
  2269. }
  2270. }
  2271. }
  2272. }
  2273. else if (!lstrcmpi(strToken, "OhSayCanYouSee"))
  2274. {
  2275. IclusterIGC* pcluster = pfsShip->GetIGCShip()->GetCluster();
  2276. if (pcluster)
  2277. {
  2278. IsideIGC* pside = pfsShip->GetIGCShip()->GetSide();
  2279. for (ModelLinkIGC* pml = pcluster->GetModels()->first(); (pml != NULL); pml = pml->next())
  2280. {
  2281. ImodelIGC* pmodel = pml->data();
  2282. if ((pmodel->GetAttributes() & c_mtStatic) != 0)
  2283. pmodel->SetSideVisibility(pside, true);
  2284. }
  2285. }
  2286. else
  2287. fIsCheat = false;
  2288. }
  2289. else if (!lstrcmpi(strToken, "IfYouBuildIt"))
  2290. {
  2291. ZString strTech;
  2292. if (pfsMission->GetStage() != STAGE_STARTED)
  2293. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2294. NA, "Trying to steal a tech before the game even starts??? Sheesh--what a loser!");
  2295. else
  2296. {
  2297. IsideIGC * pside = pfsShip->GetSide();
  2298. SideID sideid = pside->GetObjectID();
  2299. while (!(strTech = strCheat.GetToken()).IsEmpty()) // get a new token, and proceed if non-empty
  2300. {
  2301. int ibit = TechBitFromToken(strTech);
  2302. if (NA != ibit)
  2303. {
  2304. TechTreeBitMask ttbm;
  2305. ttbm.ClearAll();
  2306. ttbm.SetBit(ibit);
  2307. pside->ApplyDevelopmentTechs(ttbm);
  2308. }
  2309. else
  2310. {
  2311. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2312. NA, "Sorry, no tech found named %s.", (const char*)strTech);
  2313. }
  2314. }
  2315. }
  2316. }
  2317. else if (!lstrcmpi(strToken, "IfYouWishUponAStar"))
  2318. {
  2319. ZString strTech;
  2320. if (pfsMission->GetStage() != STAGE_STARTED)
  2321. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2322. NA, "Trying to steal a tech before the game even starts??? Sheesh--what a loser!");
  2323. else
  2324. {
  2325. IsideIGC * pside = pfsShip->GetSide();
  2326. SideID sideid = pside->GetObjectID();
  2327. TechTreeBitMask ttbm;
  2328. ttbm.SetAll();
  2329. pside->ApplyDevelopmentTechs(ttbm);
  2330. }
  2331. }
  2332. else if (!lstrcmpi(strToken, "fourteen"))
  2333. {
  2334. CFSMission * pfsMission = pfsShip->GetMission();
  2335. if (STAGE_NOTSTARTED == pfsMission->GetStage() || STAGE_STARTING == pfsMission->GetStage())
  2336. pfsMission->StartGame();
  2337. }
  2338. else if (!lstrcmpi(strToken, "fifteen"))
  2339. {
  2340. CFSMission * pfsMission = pfsShip->GetMission();
  2341. if (STAGE_NOTSTARTED == pfsMission->GetStage() || STAGE_STARTING == pfsMission->GetStage())
  2342. pfsMission->StartCountdown(15);
  2343. }
  2344. else if (!lstrcmpi(strToken, "ragnarok"))
  2345. {
  2346. // crash the server on debug builds (for testing purposes)
  2347. assert(false);
  2348. }
  2349. else if (!lstrcmpi(strToken, "gimme"))
  2350. {
  2351. ZString strMoney(strCheat.GetToken());
  2352. if (strMoney.IsEmpty()) // must specify valid station
  2353. fIsCheat = false;
  2354. else
  2355. {
  2356. BEGIN_PFM_CREATE(g.fm, pfmMoney, S, MONEY_CHANGE)
  2357. END_PFM_CREATE
  2358. pfmMoney->sidTo = pfsShip->GetShipID();
  2359. pfmMoney->sidFrom = NA;
  2360. pfmMoney->dMoney = max(-pfsShip->GetMoney(), atoi(strMoney));
  2361. pfsShip->SetMoney(pfsShip->GetMoney() + pfmMoney->dMoney);
  2362. g.fm.SendMessages(CFSSide::FromIGC(pfsShip->GetSide())->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  2363. }
  2364. }
  2365. else if (!lstrcmpi(strToken, "winwinwin"))
  2366. pfsMission->GameOver(pfsShip->GetSide(), "By a cheat");
  2367. else if (!lstrcmpi(strToken, "checkbank"))
  2368. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2369. NA, "You have %d in your bank account.", pfsShip->GetMoney());
  2370. /*
  2371. else if (!lstrcmpi(strToken, "sides"))
  2372. CheatSides(pfsShip, pfsMission);
  2373. */
  2374. else if (!lstrcmpi(strToken, "capture"))
  2375. {
  2376. ZString strStation(strCheat.GetToken());
  2377. if (strStation.IsEmpty()) // must specify valid station
  2378. fIsCheat = false;
  2379. else
  2380. {
  2381. StationID stationID = atoi(strStation);
  2382. IstationIGC * pstation = pfsMission->GetIGCMission()->GetStation(stationID);
  2383. if (pstation)
  2384. pfsShip->CaptureStation(pstation);
  2385. else
  2386. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2387. NA, "Cannot capture station %d because it does not exist.", stationID);
  2388. }
  2389. }
  2390. else if (!lstrcmpi(strToken, "createdrones"))
  2391. fIsCheat = CheatCreateDrones(pfsShip, strCheat);
  2392. #ifdef OUT_OF_ORDER
  2393. else if (!lstrcmpi(strToken, "dpgroups"))
  2394. CheatDPGroups(pfsShip);
  2395. #endif // OUT_OF_ORDER
  2396. else if (!lstrcmpi(strToken, "jpd"))
  2397. {
  2398. CheatJPD(pfsShip);
  2399. }
  2400. else if (!lstrcmpi(strToken, "fireaway"))
  2401. {
  2402. int cChats = 0;
  2403. Time timeStart = Time::Now();
  2404. while (Time::Now() - timeStart < 3.0) // let's send chat for thee seconds
  2405. {
  2406. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2407. NA, "firing away: %05d", cChats);
  2408. cChats++;
  2409. }
  2410. printf("%d chats sent in 3 seconds.\n", cChats);
  2411. pfsMission->GetSite()->SendChatf(NULL, CHAT_INDIVIDUAL_NOFILTER, pfsShip->GetIGCShip()->GetObjectID(),
  2412. NA, "%d chats sent in 3 seconds.", cChats);
  2413. }
  2414. #if defined(ALLOW_TESTAGCEVENTS_CHEAT)
  2415. else if (!lstrcmpi(strToken, "testagcevents"))
  2416. {
  2417. // Do nothing if no one is listening
  2418. AGCEventID idEvent = EventID_ChatMessage;
  2419. HAGCLISTENERS hListeners = GetAGCGlobal()->EventListeners(idEvent, -1, -1, -1);
  2420. if (!hListeners)
  2421. return true;
  2422. // Create an AGCVector (to test object persistence)
  2423. IAGCVectorPtr spVector1, spVector2;
  2424. ZSucceeded(GetAGCGlobal()->MakeAGCVector(&Vector(10, 20, 30), &spVector1));
  2425. ZSucceeded(GetAGCGlobal()->MakeAGCVector(&Vector(100, 200, 300), &spVector2));
  2426. // Get the current VariantDate
  2427. DATE date = GetLocalVariantDateTime();
  2428. // Create a CY object
  2429. CY cy = {99, 12};
  2430. LPCSTR pszContext = pfsShip->GetIGCShip() ?
  2431. pfsShip->GetIGCShip()->GetMission()->GetContextName() : NULL;
  2432. // Trigger a chat event with all types of parameters
  2433. _AGCModule.TriggerContextEvent(hListeners, idEvent, pszContext,
  2434. "TestAGCEvents cheat", -1, -1, -1, 19,
  2435. "VT_LPWSTR" , VT_LPWSTR , L"I'm a wide string",
  2436. "VT_BSTR" , VT_BSTR , (BSTR)CComBSTR(L"I'm a BSTR"),
  2437. "VT_UNKNOWN" , VT_UNKNOWN , (IAGCVector*)spVector2,
  2438. "VT_DISPATCH", VT_DISPATCH, (IAGCVector*)spVector1,
  2439. "VT_BOOL" , VT_BOOL , VARIANT_TRUE,
  2440. "VT_I1" , VT_I1 , 0x69,
  2441. "VT_I2" , VT_I2 , 0xF00D,
  2442. "VT_UI1" , VT_UI1 , 0x96,
  2443. "VT_UI2" , VT_UI2 , 0xD00F,
  2444. "VT_ERROR" , VT_ERROR , E_NOTIMPL,
  2445. "VT_R4" , VT_R4 , 1.56f,
  2446. "VT_R8" , VT_R8 , pi,
  2447. "VT_CY" , VT_CY , cy,
  2448. "VT_DATE" , VT_DATE , date,
  2449. "Message" , VT_LPSTR , "This is just a test",
  2450. "Type" , VT_LPSTR , "Cheat!",
  2451. "Target" , VT_LPSTR , "AllSrv",
  2452. "Source" , VT_I4 , -1,
  2453. "Source Name", VT_LPSTR , "Cheater");
  2454. }
  2455. #endif // defined(ALLOW_TESTAGCEVENTS_CHEAT)
  2456. else
  2457. fIsCheat = false;
  2458. return fIsCheat;
  2459. }
  2460. /*-------------------------------------------------------------------------
  2461. * HandleCOMChatEvents
  2462. *-------------------------------------------------------------------------
  2463. * Purpose:
  2464. * Generate an appropriate COM event.
  2465. *
  2466. * Parameters:
  2467. * szMessage: the main message text
  2468. * szDest: descriping who or what is receiving the message
  2469. * shipidFrom: who sent the message or NA for HQ
  2470. * pfsMission: the mission that the message was generated in
  2471. *
  2472. * Notes:
  2473. * This was broken off from ForwardChatMessage()
  2474. *
  2475. */
  2476. void HandleCOMChatEvents(const char *szMessage, const char *szDest, ShipID shipidFrom,
  2477. CFSMission * pfsMission, int iType, SoundID voiceOver, CommandID commandID)
  2478. {
  2479. const char * szSource = "Admin"; // source of the chat message
  2480. AGCUniqueID idSource = -1;
  2481. AGCUniqueID idTeam = -1;
  2482. //
  2483. // Get the GameID
  2484. //
  2485. AGCUniqueID idGame = pfsMission->GetIGCMission()->GetMissionID();
  2486. //
  2487. // Figure out source of the message
  2488. //
  2489. if (shipidFrom != NA)
  2490. {
  2491. CFSShip * pfsShipFrom = CFSShip::GetShipFromID(shipidFrom);
  2492. if (pfsShipFrom)
  2493. {
  2494. IshipIGC *pShipIGC = pfsShipFrom->GetIGCShip();
  2495. assert(pShipIGC);
  2496. szSource = pShipIGC->GetName();
  2497. idSource = -1;
  2498. if (pfsShipFrom->IsPlayer())
  2499. idSource = static_cast<USHORT>(CAdminUser::DetermineID(pfsShipFrom->GetPlayer())) | static_cast<DWORD>(AGC_AdminUser << 16);
  2500. idTeam = pShipIGC->GetSide()->GetUniqueID();
  2501. }
  2502. }
  2503. //
  2504. // Determine the Event ID to be triggered
  2505. //
  2506. AGCEventID idEvent = (CHAT_ADMIN == iType) ?
  2507. EventID_AdminPage : EventID_ChatMessage;
  2508. //
  2509. // Define the table of Chat Type strings
  2510. //
  2511. static char* s_szChatTypes[CHAT_MAX] =
  2512. {
  2513. "EVERYONE", // CHAT_EVERYONE in igc.h
  2514. "LEADERS", // CHAT_LEADERS in igc.h
  2515. "ADMIN", // CHAT_ADMIN in igc.h
  2516. "SHIP", // CHAT_SHIP in igc.h
  2517. "TEAM", // CHAT_TEAM in igc.h
  2518. "INDIVIDUAL", // CHAT_INDIVIDUAL in igc.h
  2519. "INDIVIDUAL_NOFILTER" // CHAT_INDIVIDUAL_NOFILTER in igc.h
  2520. "WING", // CHAT_WING in igc.h
  2521. "INDIVIDUAL_ECHO", // CHAT_INDIVIDUAL_ECHO in igc.h
  2522. "ALL_SECTOR", // CHAT_ALL_SECTOR in igc.h
  2523. "FRIENDLY_SECTOR", // CHAT_FRIENDLY_SECTOR in igc.h
  2524. "GROUP", // CHAT_GROUP in igc.h
  2525. "GROUP_NOECHO", // CHAT_GROUP_NOECHO in igc.h
  2526. "NOSELECTION", // CHAT_NOSELECTION in igc.h
  2527. };
  2528. // Ensure that addition/deletions to igc's enum ChatTarget are reflected here
  2529. assert(sizeofArray(s_szChatTypes) == CHAT_MAX);
  2530. assert(iType >= 0 && iType < sizeofArray(s_szChatTypes));
  2531. LPCSTR pszType = s_szChatTypes[iType];
  2532. LPCSTR pszContext = pfsMission->GetIGCMission() ?
  2533. pfsMission->GetIGCMission()->GetContextName() : NULL;
  2534. // Trigger the event if anyone is listening
  2535. _AGCModule.TriggerContextEvent(NULL, idEvent, pszContext,
  2536. szSource, idSource, idGame, idTeam, 7,
  2537. "Message" , VT_LPSTR, szMessage,
  2538. "Type" , VT_LPSTR, pszType,
  2539. "Target" , VT_LPSTR, szDest,
  2540. "GameID" , VT_I4 , idGame,
  2541. "TeamID" , VT_I4 , idTeam,
  2542. "VoiceID" , VT_I4 , voiceOver,
  2543. "CommandID" , VT_I4 , commandID);
  2544. }
  2545. bool CommandToDrone(CFSMission* pfsMission,
  2546. ChatData* pcd,
  2547. ObjectType otTarget,
  2548. ObjectID oidTarget,
  2549. const DataBuoyIGC* pdb,
  2550. CFSShip* pfsSender,
  2551. IshipIGC* pshipTo,
  2552. ImodelIGC** ppmodelTarget)
  2553. {
  2554. bool bAccepted = false;
  2555. assert (pfsSender);
  2556. IsideIGC* pside = pshipTo->GetSide();
  2557. if ((pcd->commandID != c_cidNone) &&
  2558. (pfsSender->GetIGCShip()->GetSide() == pside))
  2559. {
  2560. if (*ppmodelTarget == NULL)
  2561. {
  2562. if (pdb)
  2563. {
  2564. //Create a buoy for this chat message
  2565. *ppmodelTarget = (ImodelIGC*)(pfsMission->GetIGCMission()->CreateObject(g.timeNow, OT_buoy, pdb, sizeof(*pdb)));
  2566. assert (*ppmodelTarget);
  2567. ((IbuoyIGC*)*ppmodelTarget)->AddConsumer();
  2568. }
  2569. else
  2570. {
  2571. *ppmodelTarget = pfsMission->GetIGCMission()->GetModel(otTarget, oidTarget);
  2572. }
  2573. }
  2574. if ((pcd->commandID == c_cidDefault) && (*ppmodelTarget != NULL))
  2575. {
  2576. pcd->commandID = pshipTo->GetDefaultOrder(*ppmodelTarget);
  2577. }
  2578. if (pshipTo->LegalCommand(pcd->commandID, *ppmodelTarget))
  2579. {
  2580. bAccepted = true;
  2581. pfsMission->GetSite()->SendChat(pshipTo, CHAT_INDIVIDUAL, pfsSender->GetIGCShip()->GetObjectID(),
  2582. voAffirmativeSound, "Affirmative.");
  2583. if (pcd->commandID == c_cidJoin)
  2584. {
  2585. //Join the targets wing
  2586. assert ((*ppmodelTarget)->GetObjectType() == OT_ship);
  2587. WingID wid = ((IshipIGC*)*ppmodelTarget)->GetWingID();
  2588. pshipTo->SetWingID(wid);
  2589. BEGIN_PFM_CREATE(g.fm, pfmWingChange, CS, SET_WINGID)
  2590. END_PFM_CREATE
  2591. pfmWingChange->shipID = pshipTo->GetObjectID();
  2592. pfmWingChange->wingID = wid;
  2593. pfmWingChange->bCommanded = true;
  2594. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  2595. }
  2596. else
  2597. {
  2598. //Set both current and accepted commands
  2599. pshipTo->SetCommand(c_cmdCurrent,
  2600. *ppmodelTarget,
  2601. pcd->commandID);
  2602. pshipTo->SetCommand(c_cmdAccepted,
  2603. *ppmodelTarget,
  2604. pcd->commandID);
  2605. }
  2606. }
  2607. else
  2608. {
  2609. /*
  2610. pfsMission->GetSite()->SendChat(pshipTo, CHAT_INDIVIDUAL, pfsSender->GetIGCShip()->GetObjectID(),
  2611. voNegativeSound, "Negative.");
  2612. */
  2613. }
  2614. }
  2615. return bAccepted;
  2616. }
  2617. void ForwardChatMessageToWing(CFSMission* pfsMission,
  2618. CFSShip* pfsSender,
  2619. FEDMESSAGE* pfm,
  2620. WingID wid,
  2621. IshipIGC* pshipSkip)
  2622. {
  2623. //Send the message to everyone on the specified wing
  2624. for (ShipLinkIGC* psl = pfsSender->GetIGCShip()->GetSide()->GetShips()->first();
  2625. (psl != NULL);
  2626. psl = psl->next())
  2627. {
  2628. IshipIGC* pship = psl->data();
  2629. if ((pship->GetWingID() == wid) && (pship != pshipSkip))
  2630. {
  2631. CFSShip* pfsShipTo = (CFSShip*)(pship->GetPrivateData());
  2632. if (pfsShipTo->IsPlayer())
  2633. {
  2634. g.fm.ForwardMessage(pfsShipTo->GetPlayer()->GetConnection(), pfm, FM_GUARANTEED);
  2635. }
  2636. }
  2637. }
  2638. }
  2639. void ForwardChatMessage(CFSMission* pfsMission,
  2640. CFSShip* pfsSender,
  2641. FEDMESSAGE* pfm,
  2642. ChatData* pcd,
  2643. const char* pszMsg,
  2644. ObjectType otTarget = NA,
  2645. ObjectID oidTarget = NA,
  2646. const DataBuoyIGC* pdb = NULL)
  2647. {
  2648. if (((pcd->commandID == c_cidNone) && (pcd->voiceOver == NA) && ((pszMsg == NULL) || (*pszMsg == '\0'))) ||
  2649. (pcd->commandID < c_cidNone) ||
  2650. (pcd->commandID >= c_cidMax))
  2651. return; //Invalid chat message
  2652. if (pfsSender &&
  2653. pfsSender->IsPlayer() &&
  2654. !pfsSender->GetPlayer()->DecrementChatBudget(pcd->chatTarget != CHAT_GROUP_NOECHO))
  2655. {
  2656. return;
  2657. }
  2658. char szDestBfr[20];
  2659. const char* pszDest = "UNKNOWN";
  2660. switch (pcd->chatTarget)
  2661. {
  2662. case CHAT_EVERYONE:
  2663. {
  2664. pszDest = pfsMission->GetMissionDef()->misparms.strGameName;
  2665. if (pcd->commandID == c_cidNone)
  2666. {
  2667. g.fm.ForwardMessage(pfsMission->GetGroupMission(), pfm, FM_GUARANTEED);
  2668. }
  2669. }
  2670. break;
  2671. case CHAT_FRIENDLY_SECTOR: // can happen only after game starts
  2672. {
  2673. assert (pfsSender); // if no ship, then how can send to sector?
  2674. IclusterIGC* pcluster = pfsMission->GetIGCMission()->GetCluster(pcd->oidRecipient);
  2675. if (pcluster)
  2676. {
  2677. pszDest = pcluster->GetName();
  2678. //Send the message to everyone on the specified wing
  2679. IsideIGC* pside = pfsSender->GetIGCShip()->GetSide();
  2680. for (ShipLinkIGC* psl = pcluster->GetShips()->first();
  2681. (psl != NULL);
  2682. psl = psl->next())
  2683. {
  2684. IshipIGC* pship = psl->data();
  2685. if (pship->GetSide() == pside)
  2686. {
  2687. CFSShip* pfsShipTo = (CFSShip*)(pship->GetPrivateData());
  2688. if (pfsShipTo->IsPlayer())
  2689. {
  2690. g.fm.ForwardMessage(pfsShipTo->GetPlayer()->GetConnection(), pfm, FM_GUARANTEED);
  2691. }
  2692. else
  2693. {
  2694. //Do not send to drones
  2695. }
  2696. }
  2697. }
  2698. }
  2699. }
  2700. break;
  2701. case CHAT_ALL_SECTOR: // can happen only after game starts
  2702. {
  2703. assert (pfsSender); // if no ship, then how can send to sector?
  2704. IclusterIGC* pcluster = pfsMission->GetIGCMission()->GetCluster(pcd->oidRecipient);
  2705. if (pcluster && (pcd->commandID == c_cidNone))
  2706. {
  2707. pszDest = pcluster->GetName();
  2708. g.fm.ForwardMessage(GetGroupSectorDocked(pcluster), pfm, FM_GUARANTEED);
  2709. g.fm.ForwardMessage(GetGroupSectorFlying(pcluster), pfm, FM_GUARANTEED);
  2710. }
  2711. }
  2712. break;
  2713. case CHAT_LEADERS:
  2714. {
  2715. pszDest = "LEADERS";
  2716. int iSide = pfsMission->GetMissionDef()->misparms.nTeams;
  2717. while (iSide-- > 0)
  2718. {
  2719. CFSPlayer* pfsLeader = pfsMission->GetLeader(iSide);
  2720. if (pfsLeader && (pcd->commandID == c_cidNone))
  2721. g.fm.ForwardMessage(pfsLeader->GetConnection(), pfm, FM_GUARANTEED);
  2722. }
  2723. }
  2724. break;
  2725. case CHAT_ADMIN:
  2726. {
  2727. pszDest = "ADMIN";
  2728. //NYI
  2729. }
  2730. break;
  2731. case CHAT_SHIP:
  2732. {
  2733. pszDest = "ship";
  2734. IshipIGC* pshipSource = pfsSender->GetIGCShip()->GetSourceShip();
  2735. if (pshipSource != pfsSender->GetIGCShip())
  2736. {
  2737. CFSShip* pfsShipTo = (CFSShip*)(pshipSource->GetPrivateData());
  2738. assert (pfsShipTo->IsPlayer());
  2739. g.fm.ForwardMessage(pfsShipTo->GetPlayer()->GetConnection(), pfm, FM_GUARANTEED);
  2740. }
  2741. for (ShipLinkIGC* psl = pshipSource->GetChildShips()->first();
  2742. (psl != NULL);
  2743. psl = psl->next())
  2744. {
  2745. IshipIGC* pship = psl->data();
  2746. if (pship != pfsSender->GetIGCShip())
  2747. {
  2748. CFSShip* pfsShipTo = (CFSShip*)(pship->GetPrivateData());
  2749. assert (pfsShipTo->IsPlayer());
  2750. g.fm.ForwardMessage(pfsShipTo->GetPlayer()->GetConnection(), pfm, FM_GUARANTEED);
  2751. }
  2752. }
  2753. }
  2754. break;
  2755. case CHAT_TEAM:
  2756. {
  2757. pszDest = pfsMission->GetMissionDef()->rgszName[pcd->oidRecipient];
  2758. IsideIGC* pside = pfsMission->GetIGCMission()->GetSide(pcd->oidRecipient);
  2759. if (pside && ((pcd->commandID == c_cidNone) || (pfsSender == NULL) || (pside == pfsSender->GetSide())))
  2760. {
  2761. g.fm.ForwardMessage(CFSSide::FromIGC(pside)->GetGroup(),
  2762. pfm, FM_GUARANTEED);
  2763. }
  2764. }
  2765. break;
  2766. case CHAT_GROUP:
  2767. case CHAT_GROUP_NOECHO:
  2768. {
  2769. pcd->chatTarget = CHAT_INDIVIDUAL;
  2770. }
  2771. //No break;
  2772. case CHAT_INDIVIDUAL_NOFILTER:
  2773. case CHAT_INDIVIDUAL:
  2774. {
  2775. CFSShip * pfsShipTo = CFSShip::GetShipFromID(pcd->oidRecipient);
  2776. if (pfsShipTo) // may have gone away since the client tried to send this
  2777. {
  2778. assert(pfsShipTo->GetIGCShip());
  2779. pszDest = pfsShipTo->GetIGCShip()->GetName();
  2780. if (pfsShipTo->IsPlayer())
  2781. {
  2782. if ((pcd->commandID == c_cidNone) || (pfsSender == NULL) ||
  2783. (pfsShipTo->GetSide() == pfsSender->GetSide()))
  2784. {
  2785. CFSPlayer *pfsPlayer = pfsShipTo->GetPlayer();
  2786. assert(pfsPlayer);
  2787. g.fm.ForwardMessage(pfsPlayer->GetConnection(), pfm, FM_GUARANTEED);
  2788. }
  2789. }
  2790. else
  2791. {
  2792. ImodelIGC* pmodel = NULL;
  2793. if (CommandToDrone(pfsMission, pcd, otTarget, oidTarget, pdb, pfsSender, pfsShipTo->GetIGCShip(), &pmodel))
  2794. {
  2795. //Also echo this command to the command wing, but mark individual echo so the
  2796. //client will know how to process it.
  2797. pcd->chatTarget = CHAT_INDIVIDUAL_ECHO;
  2798. ForwardChatMessageToWing(pfsMission, pfsSender, pfm, 0, pfsSender->GetIGCShip());
  2799. }
  2800. if (pdb && pmodel)
  2801. {
  2802. assert (pmodel->GetObjectType() == OT_buoy);
  2803. ((IbuoyIGC*)pmodel)->ReleaseConsumer();
  2804. pmodel->Release();
  2805. }
  2806. }
  2807. }
  2808. }
  2809. break;
  2810. case CHAT_WING:
  2811. {
  2812. pszDest = szDestBfr;
  2813. strcpy(szDestBfr, "Wing");
  2814. _itoa(pcd->oidRecipient, &szDestBfr[4], 10);
  2815. ForwardChatMessageToWing(pfsMission, pfsSender, pfm, pcd->oidRecipient, NULL);
  2816. }
  2817. break;
  2818. }
  2819. HandleCOMChatEvents(pszMsg, pszDest, pcd->sidSender, pfsMission, pcd->chatTarget, pcd->voiceOver, pcd->commandID);
  2820. }
  2821. /*-------------------------------------------------------------------------
  2822. * CmpShipDeviation
  2823. *-------------------------------------------------------------------------
  2824. * Purpose:
  2825. * sort function for qsort for sorting ships based on deviation of
  2826. * what the actual position is and the current calculated position
  2827. * based on interpolation of inputs
  2828. *
  2829. * Parameters & Return:
  2830. * as defined by qsort. Both are actually IShipIGC*
  2831. */
  2832. int __cdecl CmpShipDeviation (const void *elem1, const void *elem2)
  2833. {
  2834. CFSShip * pship1 = * (CFSShip **) elem1;
  2835. CFSShip * pship2 = * (CFSShip **) elem2;
  2836. // we don't really expect exactly equal deviations, so don't worry about it
  2837. // We want to sort in descending order to get the largest deviations first
  2838. return (pship1->GetDeviation() < pship2->GetDeviation()) ? 1 : -1;
  2839. }
  2840. void AddUpdate(Time timeReference,
  2841. const Vector& positionReference,
  2842. CFSShip* pfsShip, ServerHeavyShipUpdate* pHeavy)
  2843. {
  2844. IshipIGC* pship = pfsShip->GetIGCShip();
  2845. pHeavy->shipID = pship->GetObjectID();
  2846. if (pfsShip->IsPlayer())
  2847. {
  2848. //If it is a player ... we are adding its update because we received a ship update for the player
  2849. const ClientShipUpdate& su = pfsShip->GetPlayer()->GetShipUpdate();
  2850. pHeavy->time.Set(timeReference, su.time);
  2851. pHeavy->position.Set(positionReference, su.position);
  2852. pHeavy->controls = su.controls;
  2853. pHeavy->orientation = su.orientation;
  2854. pHeavy->stateM = su.stateM;
  2855. pHeavy->turnRates = su.turnRates;
  2856. pHeavy->velocity = su.velocity;
  2857. pHeavy->power = su.power;
  2858. //The following is not part of the client update ...
  2859. pship->ExportFractions(&(pHeavy->fractions));
  2860. }
  2861. else
  2862. {
  2863. //It is a drone ... get the data from IGC
  2864. pship->ExportShipUpdate(timeReference, positionReference, pHeavy);
  2865. }
  2866. }
  2867. /*-------------------------------------------------------------------------
  2868. * SendShipUpdates
  2869. *-------------------------------------------------------------------------
  2870. * Purpose:
  2871. * Send ship updates to all clients in sector
  2872. */
  2873. void SendShipUpdates(CFSMission * pfsMission)
  2874. {
  2875. DWORD nFrame = pfsMission->IncrementFrame();
  2876. if (nFrame % 2 == 0)
  2877. return;
  2878. //Loop over sides and ships, queuing ship update messages as appropriate
  2879. {
  2880. ShipLinkIGC* pshipsFirst = pfsMission->GetIGCMission()->GetShips()->first();
  2881. for (SideLinkIGC* psidelink = pfsMission->GetIGCMission()->GetSides()->first();
  2882. (psidelink != NULL);
  2883. psidelink = psidelink->next())
  2884. {
  2885. IsideIGC* pside = psidelink->data();
  2886. assert(0 == g.fm.CbUsedSpaceInOutbox());
  2887. SideID sid = pside->GetObjectID();
  2888. for (ShipLinkIGC* pshiplink = pshipsFirst;
  2889. (pshiplink != NULL);
  2890. pshiplink = pshiplink->next())
  2891. {
  2892. IshipIGC* pship = pshiplink->data();
  2893. //Don't send ship status for people in the lobby
  2894. if (pship->GetSide()->GetObjectID() >= 0)
  2895. {
  2896. CFSShip* pfsShip = (CFSShip*)(pship->GetPrivateData());
  2897. ShipStatus* pss = pfsShip->GetShipStatus(sid);
  2898. ShipStatus* pssOld = pfsShip->GetOldShipStatus(sid);
  2899. if (*pss != *pssOld)
  2900. {
  2901. BEGIN_PFM_CREATE(g.fm, pfmShipStatus, S, SHIP_STATUS)
  2902. END_PFM_CREATE
  2903. pfmShipStatus->shipID = pship->GetObjectID();
  2904. pfmShipStatus->status = *pssOld = *pss; //Also update the old status
  2905. }
  2906. }
  2907. }
  2908. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  2909. }
  2910. }
  2911. const ClusterListIGC* pclstlist = pfsMission->GetIGCMission()->GetClusters();
  2912. for (ClusterLinkIGC* pcl = pfsMission->GetIGCMission()->GetClusters()->first(); (pcl != NULL); pcl = pcl->next())
  2913. {
  2914. IclusterIGC* pcluster = pcl->data();
  2915. const ShipListIGC* pshiplist = pcluster->GetShips();
  2916. const PlayerList* pplayerList = ((CFSCluster*)(pcluster->GetPrivateData()))->GetFlyingPlayers();
  2917. if ((pplayerList->n() > 0) && (pshiplist->n() > 0))
  2918. {
  2919. //We will need to send out at least some ship updates ...
  2920. //Given our bandwidth constraints & the population of the sector, figure out how many turret, heavy and light updates
  2921. //we would be able to send assuming every ship is sending a ship/turret update every c_dsecUpdateClient
  2922. assert (pshiplist->n() < c_cShipsPerSectorMax);
  2923. static CFSShip* rgShips[c_cShipsPerSectorMax];
  2924. static CFSShip* rgActiveTurrets[c_cShipsPerSectorMax];
  2925. static CFSShip* rgInactiveTurrets[c_cShipsPerSectorMax];
  2926. // Iterate once to build the array of things for which we have updates (which probably is not the total list
  2927. // of things in the cluster)
  2928. int cShipUpdates = 0;
  2929. int cActiveTurretUpdates = 0;
  2930. int cInactiveTurretUpdates = 0;
  2931. int cShips = 0;
  2932. int cTurrets = 0;
  2933. for (ShipLinkIGC * pshiplink = pshiplist->first(); (pshiplink != NULL); pshiplink = pshiplink->next())
  2934. {
  2935. IshipIGC* pship = pshiplink->data();
  2936. CFSShip* pfsShip = (CFSShip *) pship->GetPrivateData();
  2937. assert (!pfsShip->GetHasUpdate());
  2938. if (pfsShip->IsPlayer())
  2939. {
  2940. CFSPlayer* pfsPlayer = pfsShip->GetPlayer();
  2941. unsigned char ucLastUpdate = pfsPlayer->GetLastUpdate();
  2942. if (pship->GetParentShip() == NULL)
  2943. {
  2944. cShips++;
  2945. if (ucLastUpdate == c_ucShipUpdate)
  2946. {
  2947. const ClientShipUpdate& su = pfsPlayer->GetShipUpdate();
  2948. float dt = su.time - g.timeNow;
  2949. if ((dt >= -1.5f) && (dt <= 1.5f))
  2950. {
  2951. rgShips[cShipUpdates++] = pfsShip;
  2952. pfsShip->SetHasUpdate();
  2953. }
  2954. }
  2955. }
  2956. else if (pship->GetTurretID() != NA)
  2957. {
  2958. cTurrets++;
  2959. if (ucLastUpdate == c_ucActiveTurretUpdate)
  2960. {
  2961. const ClientActiveTurretUpdate& atu = pfsPlayer->GetActiveTurretUpdate();
  2962. float dt = atu.time - g.timeNow;
  2963. if ((dt >= -1.5f) && (dt <= 1.5f))
  2964. rgActiveTurrets[cActiveTurretUpdates++] = pfsShip;
  2965. }
  2966. else if ((pship->GetStateM() & (oneWeaponIGC | allWeaponsIGC)) == 0) //ucLastUpdate == c_ucInactiveTurretUpdate)
  2967. rgInactiveTurrets[cInactiveTurretUpdates++] = pfsShip;
  2968. }
  2969. pfsPlayer->ResetLastUpdate();
  2970. }
  2971. else
  2972. {
  2973. cShips++;
  2974. //Generate an update for drones every third frame. The address of the ship is as good as anything
  2975. //else for a hash function to randomly distribute drones across various updates (ignoring the low
  2976. //byte of the address since those may be block aligned)
  2977. static const DWORD c_updateFrequency = 2;
  2978. //if ( (((((DWORD)pship) >> 8) + nFrame) % c_updateFrequency) == 0)
  2979. //Make sure, for whatever reason, no ancient updates are sent from a drone
  2980. //this shouldn't happen but it could if the server gets hung for a moment.
  2981. float dt = pfsShip->GetIGCShip()->GetLastUpdate() - g.timeNow;
  2982. if ((dt >= -1.5f) && (dt <= 1.5f))
  2983. {
  2984. //This lucky ship's turn ... generate an update for it.
  2985. rgShips[cShipUpdates++] = pfsShip;
  2986. pfsShip->SetHasUpdate();
  2987. }
  2988. }
  2989. }
  2990. assert (cShips > 0);
  2991. if (cShipUpdates + cActiveTurretUpdates + cInactiveTurretUpdates > 0)
  2992. {
  2993. //Calculate how many heavy ship & active turret updates we can afford to send and still stay within our
  2994. //bandwidth constraints (where we can afford to send a max of 1500 bytes/second and the clients will send
  2995. //one update each interval)
  2996. const int cbAvailable = int(1600.0f * c_dsecUpdateClient);
  2997. //This is what we can possibly send and still remain within out constraints
  2998. const int cHeavyPerInterval = 2;
  2999. const int cLightPerInterval = 2;
  3000. const int cActivePerInterval = 2;
  3001. assert (cbAvailable > cHeavyPerInterval * sizeof(ServerHeavyShipUpdate) +
  3002. cLightPerInterval * sizeof(ServerLightShipUpdate) +
  3003. cActivePerInterval * sizeof(ServerActiveTurretUpdate));
  3004. int cLightUpdates;
  3005. int cHeavyUpdates;
  3006. if (cShipUpdates <= 2)
  3007. {
  3008. cHeavyUpdates = cShipUpdates;
  3009. cLightUpdates = 0;
  3010. }
  3011. else
  3012. {
  3013. cHeavyUpdates = 2;
  3014. cLightUpdates = (cShipUpdates * cLightPerInterval) / cShips;
  3015. if (cHeavyUpdates + cLightUpdates > cShipUpdates)
  3016. cLightUpdates = cShipUpdates - cHeavyUpdates;
  3017. }
  3018. int cTurretUpdates;
  3019. if (cActiveTurretUpdates <= 1)
  3020. {
  3021. if ((cLightUpdates > 0) && (cActiveTurretUpdates == 0))
  3022. {
  3023. //Special case ... no active turret updates so use the bandwidth to
  3024. //promote a light ship update to a heavy ship update
  3025. cHeavyUpdates++;
  3026. cLightUpdates--;
  3027. }
  3028. cTurretUpdates = cActiveTurretUpdates;
  3029. }
  3030. else
  3031. {
  3032. cTurretUpdates = (cActiveTurretUpdates == cTurrets) ? 2 : 1;
  3033. }
  3034. //OK ... given our bandwidth limits ... we think we can send the following this frame
  3035. // cTurretUpdates turret updates (active or inactive)
  3036. // cHeavyUpdates heavy ship updates
  3037. // cLightUpdates light ship updates
  3038. //
  3039. //without busting out limits/second
  3040. if (cHeavyUpdates != cShipUpdates)
  3041. {
  3042. //We can not send everything we'd like to send. Sort the list so that the people
  3043. //at the front of the list are those that really need to be sent right now
  3044. qsort(rgShips, cShipUpdates, sizeof(rgShips[0]), CmpShipDeviation);
  3045. }
  3046. {
  3047. //Adjust the deviation of the ships based on where they fall in the array
  3048. //A heavy update might get demoted to a light update (and a light to nothing)
  3049. //if a ship has a target (so that the target update can be sent as a heavy) ...
  3050. //go and treat the last heavy update as a
  3051. int effectiveHeavy = (cHeavyUpdates < cShipUpdates) ? cHeavyUpdates - 1 : cHeavyUpdates;
  3052. int effectiveLight = cHeavyUpdates +
  3053. (cHeavyUpdates + cLightUpdates < cShipUpdates)
  3054. ? (cLightUpdates - 1)
  3055. : cLightUpdates;
  3056. for (int i = 0; (i < cShipUpdates); i++)
  3057. {
  3058. if (i < effectiveHeavy)
  3059. rgShips[i]->SetDeviation(0.0f);
  3060. else
  3061. {
  3062. float r = random(0.0f, 0.25f);
  3063. rgShips[i]->SetDeviation(rgShips[i]->GetDeviation() +
  3064. random(0.0f, 0.25f) +
  3065. ((i < effectiveLight)
  3066. ? 0.70f
  3067. : 1.00f));
  3068. }
  3069. }
  3070. }
  3071. if (cTurretUpdates < cActiveTurretUpdates)
  3072. {
  3073. //We can't send all of the active turret updates so sort the list to send the ones that need
  3074. //it the most
  3075. qsort(rgActiveTurrets, cActiveTurretUpdates, sizeof(rgActiveTurrets[0]), CmpShipDeviation);
  3076. }
  3077. {
  3078. //Adjust the deviation of the ships based on where they fall in the array
  3079. for (int i = 0; (i < cActiveTurretUpdates); i++)
  3080. {
  3081. if (i < cTurretUpdates)
  3082. rgActiveTurrets[i]->SetDeviation(0.0f);
  3083. else
  3084. rgActiveTurrets[i]->SetDeviation(rgActiveTurrets[i]->GetDeviation() + random(0.5f, 0.75f));
  3085. }
  3086. }
  3087. {
  3088. //Adjust the deviation of the ships based on where they fall in the array
  3089. for (int i = 0; (i < cInactiveTurretUpdates); i++)
  3090. {
  3091. rgInactiveTurrets[i]->SetDeviation(0.0f);
  3092. }
  3093. }
  3094. //Assemble the parts of the message that are not going to change
  3095. // Use one static update message so not always calculating size and allocating/deallocating
  3096. // This is kinda tricky because we manually munge the message, rather than rely on the macros
  3097. #define CB_HSU_VARDATA(elems) ((elems) * sizeof(ServerHeavyShipUpdate))
  3098. #define CB_LSU_VARDATA(elems) ((elems) * sizeof(ServerLightShipUpdate))
  3099. #define CB_ATU_VARDATA(elems) ((elems) * sizeof(ServerActiveTurretUpdate))
  3100. #define CB_ITU_VARDATA(elems) ((elems) * sizeof(ShipID))
  3101. static char bfrHeavy[sizeof(FMD_S_HEAVY_SHIPS_UPDATE) +
  3102. CB_HSU_VARDATA(c_cShipsPerSectorMax) +
  3103. CB_ATU_VARDATA(c_cShipsPerSectorMax) +
  3104. sizeof(FMD_S_LIGHT_SHIPS_UPDATE) + //Add room for the lts ship updates to be appended to the end
  3105. CB_LSU_VARDATA(c_cShipsPerSectorMax) +
  3106. CB_ITU_VARDATA(c_cShipsPerSectorMax)];
  3107. static FMD_S_HEAVY_SHIPS_UPDATE* pfmHeavy = (FMD_S_HEAVY_SHIPS_UPDATE*)bfrHeavy;
  3108. pfmHeavy->fmid = FM_S_HEAVY_SHIPS_UPDATE;
  3109. //Use now as the reference time
  3110. pfmHeavy->timeReference = g.timeNow;
  3111. //active turret updates always start immediately after the end of the message & we will have cTurretUpdates worth
  3112. pfmHeavy->ibrgActiveTurretUpdates = sizeof(FMD_S_HEAVY_SHIPS_UPDATE);
  3113. static char bfrLight[sizeof(FMD_S_LIGHT_SHIPS_UPDATE) +
  3114. CB_LSU_VARDATA(c_cShipsPerSectorMax) +
  3115. CB_ITU_VARDATA(c_cShipsPerSectorMax)];
  3116. static FMD_S_LIGHT_SHIPS_UPDATE* pfmLight = (FMD_S_LIGHT_SHIPS_UPDATE*)bfrLight;
  3117. pfmLight->fmid = FM_S_LIGHT_SHIPS_UPDATE;
  3118. //inactive turret updates always start immediately after the end of the message & we will have cInactiveTurretUpdates worth
  3119. ShipID* pInactive = (ShipID*)(pfmLight + 1);
  3120. pfmLight->ibrgInactiveTurretUpdates = sizeof(FMD_S_LIGHT_SHIPS_UPDATE);
  3121. pfmLight->cbrgInactiveTurretUpdates = CB_ITU_VARDATA(cInactiveTurretUpdates);
  3122. {
  3123. //The same inactive messages are sent to all players ... so fill this part of the message in now
  3124. for (int i = 0; (i < cInactiveTurretUpdates); i++)
  3125. {
  3126. assert (rgInactiveTurrets[i]->IsPlayer());
  3127. *(pInactive++) = rgInactiveTurrets[i]->GetShipID();
  3128. }
  3129. }
  3130. //light ship updates come after that
  3131. pfmLight->ibrgLightShipUpdates = pfmLight->ibrgInactiveTurretUpdates + pfmLight->cbrgInactiveTurretUpdates;
  3132. //Number of light updates depends on the recipients ... so do not fill in the following fields immediately
  3133. for (PlayerLink* pplayerLink = pplayerList->first(); (pplayerLink != NULL); pplayerLink = pplayerLink->next())
  3134. {
  3135. CFSPlayer* pfsPlayer = pplayerLink->data();
  3136. IshipIGC* pshipPlayer = pfsPlayer->GetIGCShip();
  3137. {
  3138. //Fill in the stuff like the hull,
  3139. IshipIGC* pshipSource = pshipPlayer->GetSourceShip();
  3140. pshipSource->ExportFractions(&(pfmHeavy->fractions));
  3141. }
  3142. ServerActiveTurretUpdate* pActive = (ServerActiveTurretUpdate*)(pfmHeavy + 1);
  3143. {
  3144. ServerActiveTurretUpdate* pActiveMax = pActive + cTurretUpdates;
  3145. for (int i = 0; ((pActive < pActiveMax) && (i < cActiveTurretUpdates)); i++)
  3146. {
  3147. assert (i < cActiveTurretUpdates);
  3148. assert (rgActiveTurrets[i]->IsPlayer());
  3149. CFSShip* pfsShip = rgActiveTurrets[i];
  3150. if (pfsShip != pfsPlayer)
  3151. {
  3152. //The only way a player could get onto this list is if it had a recent turret update
  3153. //so forward it intact
  3154. const ClientActiveTurretUpdate& atu = pfsShip->GetPlayer()->GetActiveTurretUpdate();
  3155. pActive->time.Set(g.timeNow, atu.time);
  3156. pActive->controls = atu.controls;
  3157. pActive->orientation = atu.orientation;
  3158. (pActive++)->shipID = pfsShip->GetShipID();
  3159. }
  3160. }
  3161. }
  3162. pfmHeavy->cbrgActiveTurretUpdates = short((DWORD)pActive - (DWORD)(pfmHeavy + 1));
  3163. pfmHeavy->ibrgHeavyShipUpdates = pfmHeavy->ibrgActiveTurretUpdates + pfmHeavy->cbrgActiveTurretUpdates;
  3164. //Find the target of the player so that, if it is a ship, we can send it as a heavy if possible
  3165. IshipIGC* pshipTarget = NULL;
  3166. CFSShip* pfsTarget;
  3167. {
  3168. pfmHeavy->bpTargetHull.SetChar(255);
  3169. ImodelIGC* pmodelTarget = pshipPlayer->GetCommandTarget(c_cmdCurrent);
  3170. if (pmodelTarget && (pmodelTarget->GetCluster() == pcluster))
  3171. {
  3172. switch (pmodelTarget->GetObjectType())
  3173. {
  3174. case OT_ship:
  3175. {
  3176. IshipIGC* pship = ((IshipIGC*)pmodelTarget)->GetSourceShip();
  3177. if (pship != pshipPlayer)
  3178. {
  3179. assert (pship->GetParentShip() == NULL);
  3180. pfmHeavy->bpTargetHull = pship->GetFraction();
  3181. {
  3182. IshieldIGC* pshield = (IshieldIGC*)(pship->GetMountedPart(ET_Shield, 0));
  3183. if (pshield)
  3184. pfmHeavy->bpTargetShield = pshield->GetFraction();
  3185. }
  3186. pfsTarget = (CFSShip*)(pship->GetPrivateData());
  3187. if (pfsTarget->GetHasUpdate())
  3188. pshipTarget = pship;
  3189. }
  3190. }
  3191. break;
  3192. case OT_station:
  3193. {
  3194. IstationIGC* pstation = (IstationIGC*)pmodelTarget;
  3195. pfmHeavy->bpTargetHull = pstation->GetFraction();
  3196. pfmHeavy->bpTargetShield = pstation->GetShieldFraction();
  3197. }
  3198. break;
  3199. case OT_probe:
  3200. case OT_asteroid:
  3201. case OT_missile:
  3202. {
  3203. pfmHeavy->bpTargetHull = ((IdamageIGC*)pmodelTarget)->GetFraction();
  3204. }
  3205. }
  3206. }
  3207. }
  3208. //Reference position depends on the sender
  3209. {
  3210. IshipIGC* pshipSource = pshipPlayer->GetSourceShip();
  3211. pfmHeavy->positionReference = (pshipSource->GetCluster() == pcluster)
  3212. ? pshipSource->GetPosition()
  3213. : Vector::GetZero();
  3214. }
  3215. ServerHeavyShipUpdate* pHeavy = (ServerHeavyShipUpdate*)pActive;
  3216. ServerLightShipUpdate* pLight = (ServerLightShipUpdate*)pInactive;
  3217. {
  3218. ServerHeavyShipUpdate* pHeavyMax = pHeavy + cHeavyUpdates;
  3219. ServerLightShipUpdate* pLightMax = pLight + cLightUpdates;
  3220. if (pshipTarget)
  3221. {
  3222. assert (cHeavyUpdates > 0);
  3223. AddUpdate(g.timeNow, pfmHeavy->positionReference,
  3224. pfsTarget, pHeavy++);
  3225. }
  3226. for (int index = 0; (index < cShipUpdates); index++)
  3227. {
  3228. CFSShip* pfsShip = rgShips[index];
  3229. if (pfsShip != pfsPlayer)
  3230. {
  3231. IshipIGC* pship = pfsShip->GetIGCShip();
  3232. if (pship != pshipTarget)
  3233. {
  3234. if (pHeavy < pHeavyMax)
  3235. {
  3236. //debugf("%dH ", pship->GetObjectID());
  3237. AddUpdate(g.timeNow, pfmHeavy->positionReference, pfsShip, pHeavy++);
  3238. }
  3239. else if (pLight < pLightMax)
  3240. {
  3241. //debugf("%dL ", pship->GetObjectID());
  3242. pship->ExportShipUpdate(pLight++);
  3243. }
  3244. else
  3245. break;
  3246. }
  3247. }
  3248. }
  3249. }
  3250. pfmLight->cbrgLightShipUpdates = short((DWORD)pLight - (DWORD)pInactive);
  3251. pfmHeavy->cbrgHeavyShipUpdates = short((DWORD)pHeavy - (DWORD)pActive);
  3252. pfmLight->cbmsg = sizeof(FMD_S_LIGHT_SHIPS_UPDATE) + pfmLight->cbrgInactiveTurretUpdates + pfmLight->cbrgLightShipUpdates;
  3253. pfmHeavy->cbmsg = sizeof(FMD_S_HEAVY_SHIPS_UPDATE) + pfmHeavy->cbrgActiveTurretUpdates + pfmHeavy->cbrgHeavyShipUpdates;
  3254. //Send the heavy ship update whenever the player is flying since it has hitpoint information
  3255. if ((pfmHeavy->cbmsg != sizeof(FMD_S_HEAVY_SHIPS_UPDATE)) || (pshipPlayer->GetCluster() != NULL))
  3256. {
  3257. CB cbMsg = pfmHeavy->cbmsg;
  3258. if (pfmLight->cbmsg != sizeof(FMD_S_LIGHT_SHIPS_UPDATE))
  3259. {
  3260. //Copy the light ship update to the end of the heavy ship update
  3261. memcpy(((char*)pfmHeavy) + cbMsg, pfmLight, pfmLight->cbmsg);
  3262. cbMsg += pfmLight->cbmsg;
  3263. }
  3264. g.fm.GenericSend(pfsPlayer->GetConnection(), pfmHeavy, cbMsg, FM_NOT_GUARANTEED);
  3265. }
  3266. else if (pfmLight->cbmsg != sizeof(FMD_S_LIGHT_SHIPS_UPDATE))
  3267. {
  3268. //We should be able to ignore this case ... we should never have a case where we are sending light updates
  3269. //without sending at least one heavy update. But ... leave it in in case this assumption changes.
  3270. g.fm.ForwardMessage(pfsPlayer->GetConnection(), pfmLight, FM_NOT_GUARANTEED);
  3271. }
  3272. /*
  3273. debugf("%d %d %d %d %d\n",
  3274. Time::Now().clock(),
  3275. pfmLight->cbmsg - sizeof(FMD_S_LIGHT_SHIPS_UPDATE),
  3276. pfmHeavy->cbmsg - sizeof(FMD_S_HEAVY_SHIPS_UPDATE),
  3277. cShips, cShipUpdates);
  3278. */
  3279. }
  3280. for (int i = 0; (i < cShipUpdates); i++)
  3281. {
  3282. assert (rgShips[i]->GetHasUpdate());
  3283. rgShips[i]->ClearHasUpdate();
  3284. }
  3285. }
  3286. }
  3287. }
  3288. //
  3289. // UPDATE LATENCY: Report the counters to perfmon.
  3290. //
  3291. if (NULL != g.pServerCounters)
  3292. {
  3293. g.pServerCounters->csecAvgUpdateLatency = CFSPlayer::GetAverageLatency();
  3294. g.pServerCounters->csecMaxUpdateLatency = CFSPlayer::GetMaxLatency();
  3295. }
  3296. #undef SUVARDATASIZE
  3297. }
  3298. /*-------------------------------------------------------------------------
  3299. * MoveShips
  3300. *-------------------------------------------------------------------------
  3301. * Purpose:
  3302. * Keeps ships in constant motion
  3303. * Detects scan range entrances and exits, and sends appropriate notifications
  3304. * Sends all ship updates
  3305. */
  3306. DWORD MoveShips(CFSMission * pfsMission, Time timeNow, Time timeLastUpdate)
  3307. {
  3308. static CTempTimer timerMoveShips("in MoveShips", .1f);
  3309. timerMoveShips.Start();
  3310. if (pfsMission->GetStage() == STAGE_STARTED)
  3311. {
  3312. static CTempTimer timerSendShipUpdates("in SendShipUpdates", .1f);
  3313. timerSendShipUpdates.Start();
  3314. SendShipUpdates(pfsMission);
  3315. timerSendShipUpdates.Stop();
  3316. }
  3317. static CTempTimer timerMissionUpdate("in ImissionIGC->Update", .15f);
  3318. timerMissionUpdate.Start();
  3319. pfsMission->GetIGCMission()->Update(timeNow);
  3320. timerMissionUpdate.Stop();
  3321. timerMoveShips.Stop();
  3322. return 0;
  3323. }
  3324. /*
  3325. TODO: remove
  3326. struct ArtUpdateCallbackInfo
  3327. {
  3328. FedMessaging * pfm;
  3329. CFMConnection * pcnxnFrom;
  3330. };
  3331. // Check for new art--gets called for every artwork file changed since client last downloaded
  3332. // Our job here is simply to send one message to the client referred to in pData.
  3333. bool HandleChangedArtwork(FileChangeInfo* pfci, void* pData)
  3334. {
  3335. static DPID dpidLastPlayer = 0;
  3336. struct ArtUpdateCallbackInfo *pMyData = (struct ArtUpdateCallbackInfo *)pData;
  3337. // if a different user then last time this was called,
  3338. // then it's time to send FTP Site info
  3339. if (dpidLastPlayer != pMyData->pcnxnFrom->GetID())
  3340. {
  3341. dpidLastPlayer = pMyData->pcnxnFrom->GetID();
  3342. // All messages that follow are going to this player, and are guaranteed
  3343. g.fm.SetDefaultRecipient(pMyData->pcnxnFrom, FM_GUARANTEED);
  3344. FILETIME ftLastWriteTime = {0, 0}; // tell the client what the most recently downloaded thing is
  3345. FileChangeInfo* pFileInfo = g.pArtDirMon->GetNewestFile();
  3346. if (pFileInfo)
  3347. ftLastWriteTime = pFileInfo->ftLastWriteTime;
  3348. BEGIN_PFM_CREATE(g.fm, pfmServerInfo, S, ART_SERVER_INFO)
  3349. FM_VAR_PARM(g.szFTPServer, CB_ZTS)
  3350. FM_VAR_PARM(g.szFTPInitialDir, CB_ZTS)
  3351. FM_VAR_PARM(g.szFTPAccount, CB_ZTS)
  3352. FM_VAR_PARM(g.szFTPPassword, CB_ZTS)
  3353. END_PFM_CREATE
  3354. pfmServerInfo->ftLastArtUpdate = ftLastWriteTime;
  3355. }
  3356. // TODO: use default recipient
  3357. BEGIN_PFM_CREATE(*pMyData->pfm, pfmArtUpdate, S, ART_UPDATE)
  3358. FM_VAR_PARM(pfci->szFileName, CB_ZTS)
  3359. END_PFM_CREATE
  3360. pfmArtUpdate->cFileSize = pfci->cFileSize;
  3361. return true; // return success
  3362. }
  3363. */
  3364. /*-------------------------------------------------------------------------
  3365. * CreateTreasure(IpartIGC* p, float dv)
  3366. *-------------------------------------------------------------------------
  3367. * Purpise:
  3368. * A part was either dropped or ejected from a dead ship.
  3369. * Create the treasure on the server and export its existence
  3370. * to all clients in the mission (NYI: we really should just send
  3371. * it to the players in the sector then inform other players when
  3372. * they enter the sector).
  3373. *
  3374. * The part has a random velocity of v added to the velocity of its ship.
  3375. */
  3376. ItreasureIGC* CreateTreasure(Time now, IshipIGC* pship, DataTreasureIGC* pdt, const Vector& position, float dv)
  3377. {
  3378. assert (pship);
  3379. assert (dv > 1.0f);
  3380. pdt->objectID = pship->GetMission()->GenerateNewTreasureID();
  3381. pdt->p0 = position;
  3382. Vector direction = Vector::RandomDirection();
  3383. float radius = pship->GetRadius() + 10.0f;
  3384. pdt->p0 += radius * direction;
  3385. pdt->v0 = direction * dv + pship->GetVelocity();
  3386. IclusterIGC* pcluster = pship->GetCluster();
  3387. pdt->clusterID = pcluster->GetObjectID();
  3388. pdt->time0 = now;
  3389. ItreasureIGC* t = (ItreasureIGC *)
  3390. pship->GetMission()->CreateObject(now, OT_treasure,
  3391. pdt, sizeof(*pdt));
  3392. //Note: bad form releasing a pointer before we return it but we know it will
  3393. //stick around since it is in a cluster.
  3394. assert (t);
  3395. t->Release();
  3396. return t;
  3397. }
  3398. ItreasureIGC* CreateTreasure(Time now, IshipIGC* pship, short amount, IpartTypeIGC* ppt, const Vector& position, float dv, float lifespan = 600.0f)
  3399. {
  3400. assert (pship);
  3401. assert (ppt);
  3402. assert (dv > 1.0f);
  3403. DataTreasureIGC dt;
  3404. dt.treasureCode = c_tcPart;
  3405. dt.treasureID = ppt->GetObjectID();
  3406. dt.lifespan = lifespan;
  3407. dt.amount = amount;
  3408. dt.createNow = false;
  3409. return CreateTreasure(now, pship, &dt, position, dv);
  3410. }
  3411. ItreasureIGC* CreateTreasure(Time now, IshipIGC* pship, IpartIGC* p, IpartTypeIGC* ppt, const Vector& position, float dv, float lifespan = 600.0f)
  3412. {
  3413. assert (p);
  3414. short amount;
  3415. switch (p->GetObjectType())
  3416. {
  3417. case OT_magazine:
  3418. case OT_dispenser:
  3419. case OT_pack:
  3420. {
  3421. amount = p->GetAmount();
  3422. if (amount == 0)
  3423. return NULL;
  3424. }
  3425. break;
  3426. default:
  3427. amount = 0;
  3428. }
  3429. return CreateTreasure(now, pship, amount, ppt, position, dv, lifespan);
  3430. }
  3431. /*-------------------------------------------------------------------------
  3432. * ExportStaticIGCObjs
  3433. *-------------------------------------------------------------------------
  3434. Purpose:
  3435. Send the client all the static IGC objects. A default recipient must be already set.
  3436. Not being used. Keep it around for a while just in case we want to resurrect it.
  3437. */
  3438. void ExportStaticIGCObjs()
  3439. {
  3440. //g.fm.SetPriority(c_mcpLogonPrelim);
  3441. // Export station types
  3442. {
  3443. const StationTypeListIGC * pstlist = g.trekCore->GetStationTypes();
  3444. StationTypeLinkIGC * pstlink = NULL;
  3445. for (pstlink = pstlist->first(); pstlink; pstlink = pstlink->next())
  3446. ExportObj(pstlink->data(), OT_stationType, NULL);
  3447. }
  3448. // Export projectiles
  3449. {
  3450. const ProjectileTypeListIGC * pptlist = g.trekCore->GetProjectileTypes();
  3451. ProjectileTypeLinkIGC * pptlink = NULL;
  3452. for (pptlink = pptlist->first(); pptlink; pptlink = pptlink->next())
  3453. ExportObj(pptlink->data(), OT_projectileType, NULL);
  3454. }
  3455. // Export missile types
  3456. {
  3457. const ExpendableTypeListIGC* petlist = g.trekCore->GetExpendableTypes();
  3458. ExpendableTypeLinkIGC* petlink;
  3459. for (petlink = petlist->first(); petlink; petlink = petlink->next())
  3460. ExportObj(petlink->data(), petlink->data()->GetObjectType(), NULL);
  3461. }
  3462. // Export parts
  3463. {
  3464. const PartTypeListIGC * pptlist = g.trekCore->GetPartTypes();
  3465. PartTypeLinkIGC * pptlink = NULL;
  3466. for (pptlink = pptlist->first(); pptlink; pptlink = pptlink->next())
  3467. ExportObj(pptlink->data(), OT_partType, NULL);
  3468. }
  3469. // Export hull types
  3470. {
  3471. const HullTypeListIGC * phtlist = g.trekCore->GetHullTypes();
  3472. HullTypeLinkIGC * phtlink = NULL;
  3473. for (phtlink = phtlist->first(); phtlink; phtlink = phtlink->next())
  3474. ExportObj(phtlink->data(), OT_hullType, NULL);
  3475. }
  3476. // Export developments
  3477. {
  3478. const DevelopmentListIGC * pdtlist = g.trekCore->GetDevelopments();
  3479. DevelopmentLinkIGC * pdtlink = NULL;
  3480. for (pdtlink = pdtlist->first(); pdtlink; pdtlink = pdtlink->next())
  3481. ExportObj(pdtlink->data(), OT_development, NULL);
  3482. }
  3483. // Export drones types
  3484. {
  3485. const DroneTypeListIGC * pdtlist = g.trekCore->GetDroneTypes();
  3486. DroneTypeLinkIGC * pdtlink = NULL;
  3487. for (pdtlink = pdtlist->first(); pdtlink; pdtlink = pdtlink->next())
  3488. ExportObj(pdtlink->data(), OT_droneType, NULL);
  3489. }
  3490. // Export civilizations
  3491. {
  3492. const CivilizationListIGC * pctlist = g.trekCore->GetCivilizations();
  3493. CivilizationLinkIGC * pctlink = NULL;
  3494. for (pctlink = pctlist->first(); pctlink; pctlink = pctlink->next())
  3495. ExportObj(pctlink->data(), OT_civilization, NULL);
  3496. }
  3497. }
  3498. int FedSrvSiteBase::OnMessageBox(FedMessaging * pthis, const char * strText, const char * strCaption, UINT nType)
  3499. {
  3500. return OnMessageBox(strText, strCaption, nType);
  3501. }
  3502. int FedSrvSiteBase::OnMessageBox(const char * strText, const char * strCaption, UINT nType)
  3503. {
  3504. printf("%s:\n%s\n\n", strCaption, strText);
  3505. _AGCModule.TriggerEvent(NULL, AllsrvEventID_MessageBox, "", -1, -1, -1, 2,
  3506. "Caption", VT_LPSTR, strCaption,
  3507. "Message", VT_LPSTR, strText);
  3508. return 0; // which is intentionally not actually a valid MessageBox return code, since we're not actually throwing up a message box
  3509. }
  3510. HRESULT FedSrvSiteBase::OnSysMessage(FedMessaging * pthis)
  3511. {
  3512. if (g.pServerCounters)
  3513. {
  3514. g.pServerCounters->cSysMessagesIn++;
  3515. g.pServerCounters->cSysMessagesInPerSecond++;
  3516. }
  3517. return S_OK;
  3518. }
  3519. void FedSrvSiteBase::OnSQLErrorRecord(SSERRORINFO * perror, OLECHAR * postrError)
  3520. {
  3521. // don't make the event an error event, because this may or may not be fatal.
  3522. // But we certainly want to see them all in any case.
  3523. _AGCModule.TriggerEvent(NULL, AllsrvEventID_DatabaseErrorSQL, "", -1, -1, -1, 5,
  3524. "Message" , VT_LPWSTR, perror->pwszMessage,
  3525. "Procedure" , VT_LPWSTR, perror->pwszProcedure,
  3526. "Native" , VT_I4, perror->lNative,
  3527. "Line" , VT_I2, perror->wLineNumber,
  3528. "OleDB" , VT_LPWSTR, postrError);
  3529. }
  3530. void FedSrvSiteBase::OnOLEDBErrorRecord(BSTR bstrDescription, GUID guid, DWORD dwHelpContext,
  3531. BSTR bstrHelpFile, BSTR bstrSource)
  3532. {
  3533. // Convert the GUID to a string for the event parameter
  3534. OLECHAR szGUID[48];
  3535. StringFromGUID2(guid, szGUID, sizeofArray(szGUID));
  3536. // These are always fatal, because we have no idea what we can do about it.
  3537. _AGCModule.TriggerEvent(NULL, AllsrvEventID_DatabaseErrorOLEDB, "", -1, -1, -1, 5,
  3538. "Desc", VT_BSTR, bstrDescription,
  3539. "Guid", VT_LPWSTR,szGUID,
  3540. "HelpID", VT_I4, dwHelpContext,
  3541. "HelpFile", VT_BSTR, bstrHelpFile,
  3542. "Source", VT_BSTR, bstrSource);
  3543. }
  3544. HRESULT FedSrvSiteBase::OnNewConnection(FedMessaging * pthis, CFMConnection & cnxn)
  3545. {
  3546. // we don't do anything until they login
  3547. return S_OK;
  3548. }
  3549. HRESULT FedSrvSiteBase::OnDestroyConnection(FedMessaging * pthis, CFMConnection & cnxn)
  3550. {
  3551. CFSPlayer * pfsPlayer = CFSPlayer::GetPlayerFromConnection(cnxn);
  3552. if (pfsPlayer) // need to clean up after them if they didn't log off
  3553. {
  3554. PlaySound("AppGPFault", NULL, SND_ALIAS | SND_PURGE | SND_ASYNC);
  3555. _AGCModule.TriggerEvent(NULL, AllsrvEventID_PlayerDropped, "", -1, -1, -1, 2,
  3556. "PlayerName", VT_LPSTR, pfsPlayer->GetName(),
  3557. "ConxID", VT_I4, cnxn.GetID());
  3558. if (NULL != g.pServerCounters)
  3559. {
  3560. g.pServerCounters->cTimeouts++;
  3561. g.pServerCounters->cTimeoutsPerSecond++;
  3562. }
  3563. delete pfsPlayer;
  3564. }
  3565. return S_OK;
  3566. }
  3567. HRESULT FedSrvSiteBase::OnSessionLost(FedMessaging * pthis)
  3568. {
  3569. _AGCModule.TriggerEvent(NULL, AllsrvEventID_SessionLost, "", -1, -1, -1, 1,
  3570. "Time", VT_I4, Time::Now().clock());
  3571. // todo: gracefully shut down
  3572. return S_OK;
  3573. }
  3574. #ifndef NO_MSG_CRC
  3575. /*-------------------------------------------------------------------------
  3576. * OnBadCRC
  3577. *-------------------------------------------------------------------------
  3578. Purpose:
  3579. If someone sends a message with a bad crc, we have no choice but to nuke 'em
  3580. Parameters:
  3581. The message that failed crc
  3582. Side Effects:
  3583. Bye-bye to the offender
  3584. */
  3585. void FedSrvSiteBase::OnBadCRC(FedMessaging * pthis, CFMConnection & cnxn, BYTE * pMsg, DWORD cbMsg)
  3586. {
  3587. char buf[256];
  3588. FEDMESSAGE * pfm = (FEDMESSAGE *) pMsg;
  3589. wsprintf(buf, "HEY! We got a corrupt message!\nPlayer=%s(%d), "
  3590. "cbmsg=%d, fmid=%d, total packet size=%d.\n"
  3591. "Copy the above line to crashplayers.txt on \\\\zoneagga01. Going to drop player now.\n",
  3592. cnxn.GetName(), cnxn.GetID(),
  3593. cbMsg >= 2 ? pfm->cbmsg : 0, cbMsg >= 4 ? pfm->fmid : 0, cbMsg);
  3594. OnMessageBox(buf, "Allegiance", 0);
  3595. pthis->DeleteConnection(cnxn); // bye bye now
  3596. }
  3597. #endif
  3598. /*-------------------------------------------------------------------------
  3599. * FedSrvSiteBase.OnMessageSent
  3600. *-------------------------------------------------------------------------
  3601. Purpose:
  3602. callback to help us keep perfmon counters on data sent
  3603. */
  3604. void FedSrvSiteBase::OnMessageSent(FedMessaging * pthis, CFMRecipient * precip, const void * pv, DWORD cb, FMGuaranteed fmg)
  3605. {
  3606. int cConnections = precip->GetCountConnections();
  3607. assert (cConnections >= 0);
  3608. DWORD dwBytes = cb * cConnections;
  3609. FEDMESSAGE * pfm = (FEDMESSAGE*)pv;
  3610. if (FM_GUARANTEED == fmg)
  3611. {
  3612. // debugf("Guaranteed: %s (%u)\n", g_rgszMsgNames[pfm->fmid], cb);
  3613. if (FM_CS_ORDER_CHANGE == pfm->fmid)
  3614. {
  3615. CASTPFM(pfmOrderChange, CS, ORDER_CHANGE, pfm);
  3616. debugf("FM_CS_ORDER_CHANGE: shipID=%d, objectID=%d, objectType=%d, commandID=%d, command=%d.\n",
  3617. pfmOrderChange->shipID, pfmOrderChange->objectID, pfmOrderChange->objectType,
  3618. pfmOrderChange->commandID, pfmOrderChange->command);
  3619. }
  3620. g.pServerCounters->cPacketsOut += cConnections;
  3621. g.pServerCounters->cPacketsOutPerSecond += cConnections;
  3622. g.pServerCounters->cBytesOut += dwBytes;
  3623. g.pServerCounters->cBytesOutPerSecond += dwBytes;
  3624. /*
  3625. g.pServerCounters->cPacketsOutGPerSecond += cConnections;
  3626. g.pServerCounters->cBytesOutGPerSecond += dwBytes;
  3627. */
  3628. }
  3629. else if (FM_NOT_GUARANTEED == fmg)
  3630. {
  3631. // debugf("Unguaranteed: %s (%u)\n", g_rgszMsgNames[pfm->fmid], cb);
  3632. /*
  3633. g.pServerCounters->cPacketsOutUPerSecond += cConnections;
  3634. g.pServerCounters->cBytesOutUPerSecond += dwBytes;
  3635. */
  3636. }
  3637. }
  3638. /*
  3639. void FedSrvSiteBase::OnMessageNAK(FedMessaging * pthis, DWORD dwTime, CFMRecipient * prcp)
  3640. {
  3641. pthis->DeleteConnection(*prcp);
  3642. }
  3643. */
  3644. #define TrapHackLog(x) {if (!(x)) { LogTrapHack(EventID_HackLog , pfsPlayer, &cnxnFrom, #x, __LINE__); break; }}
  3645. #define TrapHackBoot(x) {if (!(x)) { LogTrapHack(EventID_HackBoot , pfsPlayer, &cnxnFrom, #x, __LINE__); break; }}
  3646. // REVIEW: can we get any info from the connection, like IP address?
  3647. #define TrapHackBootNoPlayer(x) {if (!(x)) { LogTrapHack(EventID_HackBootNoPlayer, NULL, &cnxnFrom, #x, __LINE__); break; }}
  3648. static bool DisplaceProbe(ImodelIGC* pmodel, Vector* position, float r)
  3649. {
  3650. Vector dxy = *position - pmodel->GetPosition();
  3651. float l2 = dxy.LengthSquared();
  3652. float rOffset = pmodel->GetRadius() + r;
  3653. if ((l2 < rOffset * rOffset) && (l2 != 0.0f))
  3654. {
  3655. *position = pmodel->GetPosition() + dxy * (rOffset / (float)sqrt(l2));
  3656. return true;
  3657. }
  3658. return false;
  3659. }
  3660. static void DisplaceProbe(IclusterIGC* pcluster, Vector* position, float r)
  3661. {
  3662. {
  3663. for (StationLinkIGC* psl = pcluster->GetStations()->first(); (psl != NULL); psl = psl->next())
  3664. {
  3665. if (DisplaceProbe(psl->data(), position, r))
  3666. return;
  3667. }
  3668. }
  3669. {
  3670. for (WarpLinkIGC* psl = pcluster->GetWarps()->first(); (psl != NULL); psl = psl->next())
  3671. {
  3672. if (DisplaceProbe(psl->data(), position, r))
  3673. return;
  3674. }
  3675. }
  3676. {
  3677. for (AsteroidLinkIGC* psl = pcluster->GetAsteroids()->first(); (psl != NULL); psl = psl->next())
  3678. {
  3679. if (DisplaceProbe(psl->data(), position, r))
  3680. return;
  3681. }
  3682. }
  3683. }
  3684. /*-------------------------------------------------------------------------
  3685. * OnAppMessage
  3686. *-------------------------------------------------------------------------
  3687. * Purpose:
  3688. * Process all incoming fedsrv messages. This is like the winproc
  3689. * of messaging. All handling of messages from clients is done here
  3690. *
  3691. * Side Effects:
  3692. * Lots
  3693. */
  3694. HRESULT FedSrvSiteBase::OnAppMessage(FedMessaging * pthis, CFMConnection & cnxnFrom, FEDMESSAGE * pfm)
  3695. {
  3696. CFSPlayer * pfsPlayer = NULL;
  3697. CFSMission * pfsMission = NULL;
  3698. ImissionIGC * pMission = NULL;
  3699. HRESULT hr = S_OK;
  3700. static CTempTimer timerOnAppMessage("in OnAppMessage", .02f);
  3701. static CTempTimer ttPreHandler("in OnAppMessage (before we actually switch)", .01f);
  3702. ttPreHandler.Start();
  3703. timerOnAppMessage.Start();
  3704. if (NULL != g.pServerCounters)
  3705. {
  3706. g.pServerCounters->cPacketsIn++;
  3707. g.pServerCounters->cPacketsInPerSecond++;
  3708. g.pServerCounters->cBytesIn += pfm->cbmsg;
  3709. g.pServerCounters->cBytesInPerSecond += pfm->cbmsg;
  3710. static Time timeLastQueueCheck = 0;
  3711. if (g.timeNow - timeLastQueueCheck > 1.0f)
  3712. {
  3713. g.fm.GetSendQueue(&(g.pServerCounters->cOutboundQueueLength),
  3714. &(g.pServerCounters->cOutboundQueueSize));
  3715. g.fm.GetReceiveQueue(&(g.pServerCounters->cInboundQueueLength),
  3716. &(g.pServerCounters->cInboundQueueSize));
  3717. timeLastQueueCheck = g.timeNow;
  3718. }
  3719. }
  3720. pfsPlayer = CFSPlayer::GetPlayerFromConnection(cnxnFrom);
  3721. if (pfsPlayer)
  3722. {
  3723. assert (pfm->fmid != FM_C_LOGONREQ); // shouldn't have a pfsPlayer yet if logging on
  3724. cnxnFrom.ResetAbsentCount();
  3725. pfsMission = pfsPlayer->GetMission();
  3726. if (pfsMission)
  3727. pMission = pfsMission->GetIGCMission();
  3728. }
  3729. ttPreHandler.Stop("...for message type %s from %s", g_rgszMsgNames[pfm->fmid],
  3730. pfsPlayer ? pfsPlayer->GetName() : "<unknown>");
  3731. if (!pfsPlayer && (pfm->fmid != FM_C_LOGONREQ))
  3732. {
  3733. _AGCModule.TriggerEvent(NULL, AllsrvEventID_MsgFromUnknownPlayer, "", -1, -1, -1, 2,
  3734. "PlayerID", VT_I4, cnxnFrom.GetID(),
  3735. "MsgName", VT_LPSTR, g_rgszMsgNames[pfm->fmid]);
  3736. }
  3737. else
  3738. switch(pfm->fmid)
  3739. {
  3740. case FM_C_SHIP_UPDATE:
  3741. {
  3742. CASTPFM(pfmShipUpdate, C, SHIP_UPDATE, pfm);
  3743. // if the cookies don't match, either they're cheating, or they haven't processed
  3744. // a message that needs processing before their updates become valid again
  3745. if (pfsPlayer->GetCookie() == pfmShipUpdate->cookie)
  3746. {
  3747. IshipIGC * pship = pfsPlayer->GetIGCShip();
  3748. if (pship->GetCluster() && (pship->GetParentShip() == NULL))
  3749. {
  3750. assert (pship->GetBaseHullType());
  3751. pfsPlayer->SetShipUpdate(pfmShipUpdate->shipupdate);
  3752. }
  3753. }
  3754. }
  3755. break;
  3756. case FM_C_ACTIVE_TURRET_UPDATE:
  3757. {
  3758. CASTPFM(pfmATU, C, ACTIVE_TURRET_UPDATE, pfm);
  3759. IshipIGC * pship = pfsPlayer->GetIGCShip();
  3760. if (pship->GetCluster() &&
  3761. (pship->GetParentShip() != NULL) &&
  3762. (pship->GetTurretID() != NA))
  3763. {
  3764. pfsPlayer->SetActiveTurretUpdate(pfmATU->atu);
  3765. }
  3766. }
  3767. break;
  3768. case FM_C_INACTIVE_TURRET_UPDATE:
  3769. {
  3770. CASTPFM(pfmITU, C, INACTIVE_TURRET_UPDATE, pfm);
  3771. IshipIGC * pship = pfsPlayer->GetIGCShip();
  3772. if (pship->GetCluster() &&
  3773. (pship->GetParentShip() != NULL) &&
  3774. (pship->GetTurretID() != NA))
  3775. {
  3776. //Stop shooting
  3777. pfsPlayer->SetInactiveTurretUpdate();
  3778. }
  3779. }
  3780. break;
  3781. case FM_CS_PING:
  3782. {
  3783. CASTPFM(pfmPing, CS, PING, pfm);
  3784. pfmPing->timeServer = g.timeNow;
  3785. //debugf("Ping from %s in at %u, ", pfsPlayer->GetName(), Time::Now ());
  3786. g.fm.SetPriority(FedMessaging::c_mcpDefault + 1);
  3787. g.fm.ForwardMessage(pfsPlayer->GetConnection(), pfmPing, pfmPing->fmg);
  3788. g.fm.SetPriority(FedMessaging::c_mcpDefault);
  3789. //debugf("out at %u\n", Time::Now ());
  3790. // Would be nice if this worked, but it always reports 500ms.
  3791. //DWORD dwLatency = g.fm.GetLatency(&cnxnFrom);
  3792. //debugf("Latency to %s=%d\n", cnxnFrom.GetName(), dwLatency);
  3793. break;
  3794. }
  3795. case FM_CS_CHATMESSAGE:
  3796. {
  3797. bool bForward = true;
  3798. CASTPFM(pfmChat, CS, CHATMESSAGE, pfm);
  3799. if (!pfsMission)
  3800. break;
  3801. TrapHackBoot(pfmChat->cbMessage >= 0);
  3802. TrapHackBoot(pfmChat->ibMessage == sizeof(FMD_CS_CHATMESSAGE));
  3803. if (pfmChat->cbMessage > 0)
  3804. {
  3805. //If you are going to cheat ... make it a public cheat
  3806. if (pfsPlayer->CanCheat() &&
  3807. DoCheatCode(pfsPlayer, FM_VAR_REF(pfmChat, Message)))
  3808. {
  3809. if (!lstrcmpi(FM_VAR_REF(pfmChat, Message), "bootme"))
  3810. break;
  3811. bForward = false;
  3812. }
  3813. else
  3814. {
  3815. //NYI hack to allow misison owner to boot people from the lobby
  3816. if (pfmChat->cbMessage > 5 && pfsMission->GetOwner() == pfsPlayer)
  3817. {
  3818. if (_strnicmp(FM_VAR_REF(pfmChat, Message), "#ban ", 5) == 0)
  3819. {
  3820. //Find a player with the given name
  3821. for (ShipLinkIGC* psl = pfsMission->GetIGCMission()->GetShips()->first(); (psl != NULL); psl = psl->next())
  3822. {
  3823. CFSShip* pfsShip = (CFSShip*)(psl->data()->GetPrivateData());
  3824. if (pfsShip->IsPlayer() && (_stricmp(FM_VAR_REF(pfmChat, Message) + 5, psl->data()->GetName()) == 0))
  3825. {
  3826. if (pfsShip->GetSide()->GetObjectID() < 0)
  3827. {
  3828. // send them to the game lobby
  3829. debugf("#ban: %s banned by %s\n", pfsShip->GetName(), pfsPlayer->GetName());
  3830. pfsMission->RemovePlayerFromMission(pfsShip->GetPlayer(), QSR_OwnerBooted);
  3831. pfmChat->cd.chatTarget = CHAT_EVERYONE;
  3832. pfmChat->cd.oidRecipient = NA;
  3833. break;
  3834. }
  3835. }
  3836. }
  3837. bForward = false;
  3838. }
  3839. }
  3840. if ((pfsPlayer->GetSide()->GetObjectID() >= 0) && (pfsMission->GetStage() == STAGE_STARTED))
  3841. {
  3842. if (pfmChat->cbMessage > strlen("#resign") && _strnicmp(FM_VAR_REF(pfmChat, Message), "#resign", strlen("#resign")) == 0)
  3843. {
  3844. pfsMission->AddBallot(new ResignBallot(pfsPlayer));
  3845. bForward = false;
  3846. }
  3847. else if (pfmChat->cbMessage > strlen("#draw") && _strnicmp(FM_VAR_REF(pfmChat, Message), "#draw", strlen("#draw")) == 0)
  3848. {
  3849. pfsMission->AddBallot(new OfferDrawBallot(pfsPlayer));
  3850. bForward = false;
  3851. }
  3852. }
  3853. }
  3854. }
  3855. pfmChat->cd.sidSender = pfsPlayer->GetIGCShip()->GetObjectID();
  3856. char* pszMsg = FM_VAR_REF(pfmChat, Message);
  3857. // If the message starts with "ICQ,playername:" then it's an icq message
  3858. // If the format doesn't match exactly, then we treat it as a regular chat
  3859. #if !defined(ALLSRV_STANDALONE)
  3860. if (pszMsg && (0 == _strnicmp("icq,", pszMsg, 4)))
  3861. {
  3862. char * szName = pszMsg + 4;
  3863. char * szColon = szName;
  3864. while (*szColon && (':' != *szColon)) //
  3865. szColon++;
  3866. if (*szColon)
  3867. {
  3868. *szColon = '\0';
  3869. lstrcpyn((char *)GetICQID_CharName, szName, sizeof(GetICQID_CharName));
  3870. SQL_GO(GetICQID);
  3871. SQLRETURN sqlret = SQL_GETROW(GetICQID);
  3872. if (*(++szColon) && SQL_SUCCESS == sqlret && GetICQID_ID != -1)
  3873. {
  3874. BEGIN_PFM_CREATE(g.fm, pfmICQChat, S, ICQ_CHAT_ACK)
  3875. FM_VAR_PARM(szColon, CB_ZTS)
  3876. END_PFM_CREATE
  3877. pfmICQChat->icqid = GetICQID_ID;
  3878. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  3879. bForward = false;
  3880. }
  3881. }
  3882. }
  3883. #endif // !defined(ALLSRV_STANDALONE)
  3884. if (bForward)
  3885. ForwardChatMessage(pfsMission, pfsPlayer, pfm,
  3886. &(pfmChat->cd),
  3887. pszMsg,
  3888. pfmChat->otTarget,
  3889. pfmChat->oidTarget,
  3890. NULL);
  3891. }
  3892. break;
  3893. case FM_CS_CHATBUOY:
  3894. {
  3895. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED))
  3896. break;
  3897. CASTPFM(pfmBuoy, CS, CHATBUOY, pfm);
  3898. pfmBuoy->cd.sidSender = pfsPlayer->GetIGCShip()->GetObjectID();
  3899. ForwardChatMessage(pfsMission, pfsPlayer, pfm,
  3900. &(pfmBuoy->cd),
  3901. FM_VAR_REF(pfmBuoy, Message),
  3902. OT_buoy,
  3903. NA,
  3904. &(pfmBuoy->db));
  3905. break;
  3906. }
  3907. case FM_CS_SET_WINGID:
  3908. {
  3909. if (!pfsMission)
  3910. break;
  3911. IsideIGC* pside = pfsPlayer->GetSide();
  3912. if (pside->GetObjectID() < 0)
  3913. break;
  3914. CASTPFM(pfmSW, CS, SET_WINGID, pfm);
  3915. TrapHackBoot(pfmSW->wingID >= 0);
  3916. TrapHackBoot(pfmSW->wingID < c_widMax);
  3917. CFSShip* pfsShip = CFSShip::GetShipFromID(pfmSW->shipID);
  3918. if (pfsShip)
  3919. {
  3920. if (pfsShip != pfsPlayer)
  3921. {
  3922. if ((pfsShip->GetSide() != pside) ||
  3923. (pfsPlayer != pfsMission->GetLeader(pside->GetObjectID())))
  3924. {
  3925. break;
  3926. }
  3927. pfmSW->bCommanded = true;
  3928. }
  3929. else
  3930. pfmSW->bCommanded = false; //If sent by a player, it should already be set correctly on the client
  3931. //pfmSW->shipID = pfsPlayer->GetShipID();
  3932. g.fm.ForwardMessage(CFSSide::FromIGC(pside)->GetGroup(), pfmSW, FM_GUARANTEED);
  3933. pfsShip->GetIGCShip()->SetWingID(pfmSW->wingID);
  3934. }
  3935. }
  3936. break;
  3937. case FM_C_VOTE:
  3938. {
  3939. // you must be on a valid team to vote.
  3940. if (!pfsMission || pfsPlayer->GetSide()->GetObjectID() < 0)
  3941. break;
  3942. CASTPFM(pfmVote, C, VOTE, pfm);
  3943. pfsMission->TallyVote(pfsPlayer, pfmVote->ballotID, pfmVote->bAgree);
  3944. }
  3945. break;
  3946. case FM_CS_REQUEST_MONEY:
  3947. {
  3948. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED))
  3949. break;
  3950. CASTPFM(pfmRequest, CS, REQUEST_MONEY, pfm);
  3951. TrapHackBoot(pfmRequest->amount > 0);
  3952. if (!pfsPlayer->DecrementChatBudget(true))
  3953. {
  3954. break;
  3955. }
  3956. IshipIGC* pshipAD = pfsPlayer->GetIGCShip()->GetAutoDonate();
  3957. if ((!pshipAD) || (((CFSShip*)(pshipAD->GetPrivateData()))->GetMoney() < pfmRequest->amount))
  3958. {
  3959. //Not autodonating to anyone ... find the person on the team with the most money
  3960. Money moneyMax = pfmRequest->amount - 1;
  3961. IsideIGC* pside = pfsPlayer->GetSide();
  3962. for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = psl->next())
  3963. {
  3964. IshipIGC* pship = psl->data();
  3965. if (pship != pfsPlayer->GetIGCShip())
  3966. {
  3967. CFSShip* pfsship = (CFSShip*)(pship->GetPrivateData());
  3968. if (pfsship->GetMoney() > moneyMax)
  3969. {
  3970. moneyMax = pfsship->GetMoney();
  3971. pshipAD = pship;
  3972. }
  3973. }
  3974. }
  3975. }
  3976. if (pshipAD)
  3977. {
  3978. pfmRequest->shipidRequest = pfsPlayer->GetShipID();
  3979. CFSShip* pfsAD = (CFSShip*)(pshipAD->GetPrivateData());
  3980. g.fm.ForwardMessage(pfsAD->GetPlayer()->GetConnection(), pfmRequest, FM_GUARANTEED);
  3981. }
  3982. }
  3983. break;
  3984. case FM_CS_FIRE_MISSILE:
  3985. {
  3986. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED))
  3987. break;
  3988. CASTPFM(pfmFireMissile, CS, FIRE_MISSILE, pfm);
  3989. DataMissileIGC dm;
  3990. dm.pLauncher = pfsPlayer->GetIGCShip();
  3991. TrapHackBoot(dm.pLauncher->GetParentShip() == NULL);
  3992. ImagazineIGC* pmagazine = (ImagazineIGC*)(dm.pLauncher->GetMountedPart(ET_Magazine, 0));
  3993. if (pmagazine)
  3994. {
  3995. TrapHackBoot(pmagazine->GetAmount() > 0);
  3996. //Speed hack ... use the player's cluster if appropriate and the cluster from the missile
  3997. //message if not.
  3998. IclusterIGC* pcluster = pfsPlayer->GetCluster();
  3999. if ((!pcluster) || (pcluster->GetObjectID() != pfmFireMissile->clusterID))
  4000. pcluster = pMission->GetCluster(pfmFireMissile->clusterID);
  4001. TrapHackBoot (pcluster);
  4002. Time timeFudge = pfmFireMissile->timeFired + 0.5f;
  4003. pfmFireMissile->bDud = (timeFudge < pmagazine->GetTimeLoaded());
  4004. {
  4005. if (pfmFireMissile->bDud)
  4006. debugf("dud missile %d %d %d\n", timeFudge.clock(), pmagazine->GetTimeLoaded().clock());
  4007. }
  4008. //Ignore the IDs provided by the client and, instead, generate one ourselves.
  4009. pfmFireMissile->launcherID = pfsPlayer->GetShipID();
  4010. dm.pmissiletype = pmagazine->GetMissileType();
  4011. assert (dm.pmissiletype);
  4012. int iNumMissiles = pfmFireMissile->cbmissileLaunchData / sizeof(MissileLaunchData);
  4013. MissileLaunchData* pMissileLaunchData = (MissileLaunchData*) (FM_VAR_REF(pfmFireMissile, missileLaunchData));
  4014. pfmFireMissile->missiletypeID = dm.pmissiletype->GetObjectID();
  4015. const Orientation& myOrientation = dm.pLauncher->GetOrientation();
  4016. Vector position = dm.pLauncher->GetPosition() + (pmagazine->GetEmissionPt() * myOrientation);
  4017. const Vector& myVelocity = dm.pLauncher->GetVelocity();
  4018. Vector velocity = myVelocity - dm.pmissiletype->GetInitialSpeed() * myOrientation.GetBackward();
  4019. if (pfmFireMissile->targetType == NA)
  4020. dm.pTarget = NULL;
  4021. else
  4022. {
  4023. dm.pTarget = pMission->GetModel(pfmFireMissile->targetType, pfmFireMissile->targetID);
  4024. if (dm.pTarget &&
  4025. ((dm.pTarget->GetCluster() != pcluster) ||
  4026. !pfsPlayer->GetIGCShip()->CanSee(dm.pTarget)))
  4027. dm.pTarget = NULL;
  4028. }
  4029. dm.pCluster = pcluster;
  4030. dm.lock = pfmFireMissile->lock;
  4031. for (int i = 0; i < iNumMissiles; i++)
  4032. {
  4033. {
  4034. Vector d = position - pMissileLaunchData[i].vecPosition;
  4035. float d2 = d * d;
  4036. if (d2 > 10.0f)
  4037. debugf("missile position error %f %s\n", d2, pfsPlayer->GetName());
  4038. if ((d2 < 0.0f) || (d2 > 200.0f * 200.0f))
  4039. pMissileLaunchData[i].vecPosition = position;
  4040. }
  4041. {
  4042. Vector d = velocity - pMissileLaunchData[i].vecVelocity;
  4043. float d2 = d * d;
  4044. if (d2 > 10.0f)
  4045. debugf("missile velocity error %f\n", d2);
  4046. if ((d2 < 0.0f) || (d2 > 100.0f * 100.0f))
  4047. pMissileLaunchData[i].vecVelocity = velocity;
  4048. }
  4049. {
  4050. float d2 = pMissileLaunchData[i].vecForward * pMissileLaunchData[i].vecForward;
  4051. if ((d2 < 0.95f * 0.95f) || (d2 > 1.05f * 1.05f))
  4052. pMissileLaunchData[i].vecForward = myOrientation.GetForward();
  4053. }
  4054. dm.position = pMissileLaunchData[i].vecPosition;
  4055. dm.velocity = pMissileLaunchData[i].vecVelocity;
  4056. dm.forward = pMissileLaunchData[i].vecForward;
  4057. dm.missileID = pMissileLaunchData[i].missileID = pMission->GenerateNewMissileID ();
  4058. dm.bDud = pfmFireMissile->bDud;
  4059. ImissileIGC * m = (ImissileIGC*)(pMission->CreateObject(pfmFireMissile->timeFired + dm.pmissiletype->GetReadyTime(), OT_missile, &dm, sizeof(dm)));
  4060. if (m)
  4061. m->Release();
  4062. }
  4063. //Tell everyone else to fire
  4064. g.fm.ForwardMessage(GetGroupSectorFlying(pcluster), pfmFireMissile, FM_GUARANTEED);
  4065. pmagazine->SetTimeFired(pfmFireMissile->timeFired);
  4066. pmagazine->SetLock(0.0f);
  4067. short amount = pmagazine->GetAmount() - iNumMissiles;
  4068. pmagazine->SetAmount(amount);
  4069. }
  4070. }
  4071. break;
  4072. case FM_C_AUTODONATE:
  4073. {
  4074. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED))
  4075. break;
  4076. CASTPFM(pfmAutoDonate, C, AUTODONATE, pfm);
  4077. TrapHackBoot(pfmAutoDonate >= 0);
  4078. TrapHackBoot(pfsPlayer->GetMoney() >= pfmAutoDonate->amount);
  4079. IsideIGC* pside = pfsPlayer->GetSide();
  4080. CFSPlayer* pfsDonate = NULL;
  4081. ShipID shipID = pfsPlayer->GetShipID();
  4082. if ((pfmAutoDonate->sidDonateTo != NA) && (pfmAutoDonate->sidDonateTo != shipID))
  4083. {
  4084. CFSShip* pfsDonateTo = CFSShip::GetShipFromID(pfmAutoDonate->sidDonateTo);
  4085. if (pfsDonateTo && pfsDonateTo->IsPlayer() && (pside == pfsDonateTo->GetSide()))
  4086. {
  4087. pfsDonate = pfsDonateTo->GetPlayer();
  4088. }
  4089. }
  4090. pfsPlayer->SetAutoDonate(pfsDonate, pfmAutoDonate->amount);
  4091. if (pMission->GetMissionStage() == STAGE_STARTED)
  4092. {
  4093. IshipIGC* pshipNewDonate = pfsPlayer->GetIGCShip()->GetAutoDonate();
  4094. SideID sideID = pside->GetObjectID();
  4095. IshipIGC* pshipLeader = pfsMission->GetLeader(sideID)->GetIGCShip();
  4096. if (pshipLeader != pshipNewDonate)
  4097. {
  4098. const ShipListIGC* pships = pside->GetShips();
  4099. //Donated to someone other than the existing leader ... possible
  4100. //leader ship change ... see if someone has more votes than the leader
  4101. int cOther = 0;
  4102. int cLeader = 0;
  4103. {
  4104. for (ShipLinkIGC* psl = pships->first(); (psl != NULL); psl = psl->next())
  4105. {
  4106. IshipIGC* pshipAD = psl->data()->GetAutoDonate();
  4107. if (pshipAD)
  4108. {
  4109. if (pshipAD == pshipLeader)
  4110. cLeader++;
  4111. else
  4112. cOther++;
  4113. }
  4114. }
  4115. }
  4116. if (cLeader < cOther)
  4117. {
  4118. //Leader has less than the possible votes against
  4119. //Possible change of command
  4120. //Who has more votes than the leader?
  4121. IshipIGC* pshipNewLeader = PickNewLeader(pships, pshipLeader, cLeader);
  4122. if (pshipNewLeader)
  4123. {
  4124. // if there is a human player still on their team with the given ship ID,
  4125. // make that new player the leader.
  4126. CFSShip* pfsNewLeader = (CFSShip*)(pshipNewLeader->GetPrivateData());
  4127. pfsMission->SetLeader(pfsNewLeader->GetPlayer());
  4128. }
  4129. }
  4130. }
  4131. }
  4132. }
  4133. break;
  4134. case FM_C_FIRE_EXPENDABLE:
  4135. {
  4136. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED))
  4137. break;
  4138. CASTPFM(pfmFireExpendable, C, FIRE_EXPENDABLE, pfm);
  4139. IshipIGC* pShip = pfsPlayer->GetIGCShip();
  4140. assert (pShip);
  4141. TrapHackBoot(pShip->GetParentShip() == NULL);
  4142. IdispenserIGC* pdispenser = (IdispenserIGC*)(pShip->GetMountedPart(pfmFireExpendable->et, 0));
  4143. if (pdispenser)
  4144. {
  4145. TrapHackBoot(pdispenser->GetAmount() > 0);
  4146. IexpendableTypeIGC* pet = pdispenser->GetExpendableType();
  4147. IclusterIGC* pcluster = pShip->GetCluster();
  4148. if (pcluster)
  4149. {
  4150. assert(0 == g.fm.CbUsedSpaceInOutbox());
  4151. {
  4152. BEGIN_PFM_CREATE(g.fm, pfmSFE, S, FIRE_EXPENDABLE)
  4153. END_PFM_CREATE
  4154. pfmSFE->launcherID = pShip->GetObjectID();
  4155. pfmSFE->equipmentType = pfmFireExpendable->et;
  4156. }
  4157. const Vector& myPosition = pShip->GetPosition();
  4158. const Vector& myVelocity = pShip->GetVelocity();
  4159. const Orientation& myOrientation = pShip->GetOrientation();
  4160. ObjectType type = pet->GetObjectType();
  4161. if (type == OT_chaffType)
  4162. {
  4163. //Drop the chaff "behind" the player's ship
  4164. DataChaffIGC dataChaff;
  4165. dataChaff.time0 = g.timeNow;
  4166. dataChaff.p0 = myPosition;
  4167. dataChaff.v0 = myVelocity + myOrientation.GetUp() * 5.0f;
  4168. dataChaff.pcluster = pcluster;
  4169. dataChaff.pchafftype = (IchaffTypeIGC*)pet;
  4170. {
  4171. BEGIN_PFM_CREATE(g.fm, pfmCC, S, CREATE_CHAFF)
  4172. END_PFM_CREATE
  4173. pfmCC->p0 = dataChaff.p0;
  4174. pfmCC->v0 = dataChaff.v0;
  4175. pfmCC->time0 = dataChaff.time0;
  4176. pfmCC->etid = pet->GetObjectID();
  4177. }
  4178. IchaffIGC* c = (IchaffIGC*)(pMission->CreateObject(g.timeNow,
  4179. OT_chaff,
  4180. &dataChaff,
  4181. sizeof(dataChaff)));
  4182. assert (c != NULL);
  4183. //Confuse any missiles lauched at the player's ship
  4184. for (MissileLinkIGC* pml = pcluster->GetMissiles()->first();
  4185. (pml != NULL);
  4186. pml = pml->next())
  4187. {
  4188. ImissileIGC* pmissile = pml->data();
  4189. if (pmissile->GetTarget() == pShip)
  4190. {
  4191. //A missing aimed at me ... do the chaff work?
  4192. float chaff = ((IchaffTypeIGC*)pet)->GetChaffStrength();
  4193. float missile = pmissile->GetMissileType()->GetChaffResistance();
  4194. //The following is equivalent to random(0, chaff) > random(0, missile)
  4195. float cm = chaff * missile;
  4196. float f = (chaff > missile)
  4197. ? (cm - 0.5f * missile * missile)
  4198. : (0.5f * chaff * chaff);
  4199. if (random(0.0f, cm) <= f)
  4200. {
  4201. //Missile lost lock
  4202. pmissile->SetTarget(c);
  4203. BEGIN_PFM_CREATE(g.fm, pfmSpoof, S, MISSILE_SPOOFED)
  4204. END_PFM_CREATE
  4205. pfmSpoof->missileID = pmissile->GetObjectID();
  4206. }
  4207. }
  4208. }
  4209. BEGIN_PFM_CREATE(g.fm, pfmSpoof, S, END_SPOOFING)
  4210. END_PFM_CREATE
  4211. c->Release();
  4212. }
  4213. else
  4214. {
  4215. float speed2 = myVelocity.LengthSquared();
  4216. float offset = (pet->GetRadius() + pShip->GetRadius()) + 5.0f;
  4217. Vector displace = (speed2 < 1.0f)
  4218. ? (myOrientation.GetBackward() * offset)
  4219. : (myVelocity * (-offset / float(sqrt(speed2))));
  4220. Vector position = myPosition + displace;
  4221. IsideIGC* pside = pShip->GetSide();
  4222. if (type == OT_mineType)
  4223. {
  4224. DataMineIGC dm;
  4225. dm.pshipLauncher = pShip;
  4226. dm.psideLauncher = pside;
  4227. dm.mineID = pMission->GenerateNewMineID();
  4228. dm.time0 = pShip->GetLastUpdate() + 3.0f; //3 second delay
  4229. dm.p0 = position;
  4230. dm.pminetype = (ImineTypeIGC*)pet;
  4231. assert (dm.pminetype);
  4232. dm.pcluster = pcluster;
  4233. dm.exportF = false;
  4234. ImineIGC * m = (ImineIGC*)(pMission->CreateObject(
  4235. g.timeNow, OT_mine, &dm, sizeof(dm)));
  4236. if (m)
  4237. m->Release();
  4238. }
  4239. else
  4240. {
  4241. assert (type == OT_probeType);
  4242. DataProbeIGC dp;
  4243. dp.pside = pShip->GetSide();
  4244. dp.pship = pShip;
  4245. dp.probeID = pMission->GenerateNewProbeID();
  4246. dp.time0 = pShip->GetLastUpdate();
  4247. // Potentially unsafe
  4248. // DisplaceProbe(pcluster, &position, pet->GetRadius());
  4249. dp.p0 = position;
  4250. dp.pprobetype = (IprobeTypeIGC*)pet;
  4251. assert (dp.pprobetype);
  4252. dp.pcluster = pcluster;
  4253. dp.exportF = false;
  4254. dp.pmodelTarget = pShip->GetCommandTarget(c_cmdCurrent);
  4255. IprobeIGC * p = (IprobeIGC*)(pMission->CreateObject(
  4256. g.timeNow, OT_probe, &dp, sizeof(dp)));
  4257. if (p)
  4258. p->Release();
  4259. }
  4260. }
  4261. short amount = pdispenser->GetAmount() - 1;
  4262. assert (amount >= 0);
  4263. pdispenser->SetAmount(amount);
  4264. g.fm.SendMessages(GetGroupSectorFlying(pcluster), FM_GUARANTEED, FM_FLUSH);
  4265. }
  4266. }
  4267. }
  4268. break;
  4269. case FM_CS_LOGOFF:
  4270. {
  4271. if (NULL != g.pServerCounters)
  4272. g.pServerCounters->cLogoffs++;
  4273. debugf("Logoff: %s(%u), was on side=%d mission=%x.\n",
  4274. pfsPlayer->GetName(), pfsPlayer->GetConnection()->GetID(),
  4275. (pfsPlayer->GetSide() ? pfsPlayer->GetSide()->GetObjectID() : NA),
  4276. (pfsPlayer->GetMission() ? pfsPlayer->GetMission()->GetCookie() : NA));
  4277. g.fm.ForwardMessage(pfsPlayer->GetConnection(), pfm, FM_GUARANTEED);
  4278. delete pfsPlayer; //need to delete here, instead of in OnDestroyConnection, so we can tell when we get there whether it was a dropped connection
  4279. }
  4280. break;
  4281. case FM_C_LOGONREQ:
  4282. {
  4283. CQLogonStats * pquery = new CQLogonStats(GotLogonDetails);
  4284. CQLogonStatsData * pqd = pquery->GetData();
  4285. if (NULL != g.pServerCounters)
  4286. g.pServerCounters->cLoginAttempts++;
  4287. CASTPFM(pfmLogon, C, LOGONREQ, pfm);
  4288. char * szReason = "Sorry Charlie. Something \"weird\" happened."; // default failure message
  4289. bool fRetry = false;
  4290. // sanity check the message
  4291. TrapHackBootNoPlayer(NULL != FM_VAR_REF(pfmLogon, CharacterName)
  4292. && NULL != memchr(FM_VAR_REF(pfmLogon, CharacterName), 0, c_cbName));
  4293. TrapHackBootNoPlayer(NULL == FM_VAR_REF(pfmLogon, MissionPassword)
  4294. || NULL != memchr(FM_VAR_REF(pfmLogon, MissionPassword), 0, c_cbGamePassword));
  4295. TrapHackBootNoPlayer(NULL != FM_VAR_REF(pfmLogon, CDKey)
  4296. && NULL != memchr(FM_VAR_REF(pfmLogon, CDKey), 0, c_cbCDKey));
  4297. if (pfmLogon->fedsrvVer == MSGVER)
  4298. { // ok, they have the right version of the client
  4299. // Now we need to look them up to make sure they logged in
  4300. bool fValid = false; // whether we have a valid character
  4301. LPBYTE pZoneTicket = (LPBYTE) FM_VAR_REF(pfmLogon, ZoneTicket);
  4302. #if !defined(ALLSRV_STANDALONE)
  4303. if (pZoneTicket) // it's all in the Zone Ticket
  4304. {
  4305. hr = g.pzas->DecryptTicket(pZoneTicket, pfmLogon->cbZoneTicket);
  4306. // Since they came in through the lobby, their token MUST be valid, but since we're paranoid, let's check again
  4307. switch (hr)
  4308. {
  4309. case ZT_NO_ERROR:
  4310. {
  4311. if (lstrcmpi(g.strAuthServer, g.pzas->GetAuthServer())) // you MUST use the auth server that we expect
  4312. {
  4313. szReason = "Your account authentication did not go through the expected server.";
  4314. break;
  4315. }
  4316. bool fValidNow = false;
  4317. fValid = g.pzas->HasToken(g.m_szToken, &fValidNow);
  4318. if (!fValid)
  4319. szReason = "Your Allegiance Zone subscription has expired.";
  4320. else if (!fValidNow)
  4321. {
  4322. fValid = false; // not considered valid anymore
  4323. szReason = "This account is not authorized to play on the Allegiance Zone.";
  4324. fRetry = true;
  4325. }
  4326. break;
  4327. }
  4328. case ZT_E_BUFFER_TOO_SMALL:
  4329. _AGCModule.TriggerEvent(NULL, AllsrvEventID_IncreaseTokensMax, "", -1, -1, -1, 0);
  4330. break;
  4331. case ZT_E_AUTH_INVALID_TICKET:
  4332. _AGCModule.TriggerEvent(NULL, AllsrvEventID_InvalidZoneTicket, "", -1, -1, -1, 0);
  4333. szReason = "Could not validate Zone ID.";
  4334. break;
  4335. default:
  4336. _AGCModule.TriggerEvent(NULL, AllsrvEventID_DecryptTicketFailed, "", -1, -1, -1, 0);
  4337. }
  4338. }
  4339. else
  4340. {
  4341. _AGCModule.TriggerEvent(NULL, AllsrvEventID_NoTicket, "", -1, -1, -1, 0);
  4342. szReason = "No login credentials found.";
  4343. }
  4344. lstrcpy(pqd->szCharacterName, g.pzas->GetName());
  4345. pqd->characterID = g.pzas->GetAccountID();
  4346. #else // !defined(ALLSRV_STANDALONE)
  4347. if (0 < pfmLogon->cbCharacterName && pfmLogon->cbCharacterName <= c_cbName)
  4348. {
  4349. fValid = true;
  4350. lstrcpy(pqd->szCharacterName, FM_VAR_REF(pfmLogon, CharacterName));
  4351. pqd->characterID = GetNextCharacterID();
  4352. pqd->fCanCheat = false;
  4353. }
  4354. else
  4355. {
  4356. _AGCModule.TriggerEvent(NULL, AllsrvEventID_NoTicket, "", -1, -1, -1, 0);
  4357. szReason = "No player name found.";
  4358. lstrcpy(pqd->szCharacterName, "<invalid>");
  4359. }
  4360. #endif // !defined(ALLSRV_STANDALONE)
  4361. lstrcpy(pqd->szReason, szReason);
  4362. pqd->fRetry = fRetry;
  4363. pqd->dwCookie = pfmLogon->dwCookie;
  4364. if(FM_VAR_REF(pfmLogon, MissionPassword))
  4365. lstrcpy(pqd->szPassword, FM_VAR_REF(pfmLogon, MissionPassword));
  4366. else
  4367. pqd->szPassword[0] = 0;
  4368. lstrcpy(pqd->szCDKey, FM_VAR_REF(pfmLogon, CDKey));
  4369. pqd->fValid = fValid;
  4370. pqd->dwConnectionID = cnxnFrom.GetID();
  4371. #if !defined(ALLSRV_STANDALONE)
  4372. if (fValid)
  4373. g.sql.PostQuery(pquery);
  4374. else // pquery->OnDataReady() below is the else of this
  4375. #endif
  4376. pquery->DataReady();
  4377. } // if correct messaging version
  4378. else
  4379. {
  4380. if (NULL != g.pServerCounters)
  4381. g.pServerCounters->cLoginsFailed++;
  4382. char szVerError[350];
  4383. if (pfmLogon->fedsrvVer < MSGVER)
  4384. wsprintf(szVerError, "The server you are trying to play on has a newer version than you. "
  4385. "Please go play online to get the latest auto-update. If this doesn't work, try deleting "
  4386. "the file 'filelist.txt' from the install directory and restarting the application.");
  4387. else /* if (pfmLogon->fedsrvVer > MSGVER) */
  4388. {
  4389. #if defined(ALLSRV_STANDALONE)
  4390. wsprintf(szVerError, "The server you are trying to play on is out-of-date. Find another server "
  4391. "or contact the server's owner and tell them to auto-update by connecting to the zone match-making "
  4392. "service. If that doesn't work, have the server's owner try deleting "
  4393. "the file 'filelist.txt' from the install directory and restarting the server.");
  4394. #else
  4395. wsprintf(szVerError, "The server you are trying to play on is out-of-date. The Zone needs to update their game server(s). Please try again later.");
  4396. #endif
  4397. }
  4398. assert(lstrlen(szVerError) < sizeof(szVerError));
  4399. _AGCModule.TriggerEvent(NULL, AllsrvEventID_BadClientVersion, "", -1, -1, -1, 2,
  4400. "ServerVer", VT_I4, MSGVER,
  4401. "ClientVer", VT_I4, pfmLogon->fedsrvVer);
  4402. BEGIN_PFM_CREATE(g.fm, pfmLogonAck, S, LOGONACK)
  4403. FM_VAR_PARM(szVerError, CB_ZTS)
  4404. FM_VAR_PARM(NULL, CB_ZTS)
  4405. END_PFM_CREATE
  4406. pfmLogonAck->fValidated = false;
  4407. pfmLogonAck->fRetry = false;
  4408. g.fm.SendMessages(&cnxnFrom, FM_GUARANTEED, FM_FLUSH);
  4409. }
  4410. #ifdef DEBUG
  4411. DWORD cMsgs, cbData;
  4412. ZSucceeded(g.fm.GetSendQueue(&cMsgs, &cbData));
  4413. debugf("GetSendQueue reports %d msgs, %d bytes.\n", cMsgs, cbData);
  4414. #endif
  4415. break;
  4416. }
  4417. case FM_C_TREASURE_ACK:
  4418. {
  4419. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED))
  4420. break;
  4421. if (pfsPlayer->GetTreasureObjectID() != NA)
  4422. {
  4423. IsideIGC* pside = pfsPlayer->GetSide();
  4424. if (pside->GetObjectID() >= 0)
  4425. {
  4426. TrapHackBoot(pfsPlayer->GetIGCShip()->GetParentShip() == NULL);
  4427. CASTPFM(pfmTA, C, TREASURE_ACK, pfm);
  4428. ObjectID oid = pfsPlayer->GetTreasureObjectID();
  4429. IpartTypeIGC* ppt = pMission->GetPartType(oid);
  4430. assert(ppt);
  4431. short amount = pfsPlayer->GetTreasureAmount();
  4432. if (pfmTA->mountID != c_mountNA)
  4433. {
  4434. TrapHackBoot(pfsPlayer->GetIGCShip()->GetHullType()->CanMount(ppt, pfmTA->mountID));
  4435. IpartIGC* ppart = pfsPlayer->GetIGCShip()->GetMountedPart(ppt->GetEquipmentType(), pfmTA->mountID);
  4436. if (ppart)
  4437. {
  4438. TrapHackBoot(ppart->GetPartType() == ppt);
  4439. ppart->SetAmount(ppart->GetAmount() + amount);
  4440. }
  4441. else
  4442. pfsPlayer->GetIGCShip()->CreateAndAddPart(ppt, pfmTA->mountID, amount);
  4443. IclusterIGC* pcluster = pfsPlayer->GetIGCShip()->GetCluster();
  4444. if (pcluster)
  4445. {
  4446. BEGIN_PFM_CREATE(g.fm, pfmAddPart, S, ADD_PART)
  4447. END_PFM_CREATE
  4448. pfmAddPart->shipID = pfsPlayer->GetShipID();
  4449. pfmAddPart->newPartData.partID = oid;
  4450. pfmAddPart->newPartData.mountID = pfmTA->mountID;
  4451. pfmAddPart->newPartData.amount = amount;
  4452. g.fm.SendMessages(GetGroupSectorFlying(pcluster), FM_GUARANTEED, FM_FLUSH);
  4453. }
  4454. }
  4455. else if (pfsPlayer->GetIGCShip()->GetCluster())
  4456. {
  4457. CreateTreasure(g.timeNow, pfsPlayer->GetIGCShip(), amount, ppt,
  4458. pfsPlayer->GetIGCShip()->GetPosition(), 25.0f, 120.0f);
  4459. }
  4460. }
  4461. pfsPlayer->SetTreasureObjectID(NA);
  4462. }
  4463. }
  4464. break;
  4465. case FM_C_BUY_LOADOUT:
  4466. {
  4467. if (!pfsMission
  4468. || (pfsPlayer->GetIGCShip()->GetCluster() != NULL)
  4469. || (pfsPlayer->GetIGCShip()->GetStation() == NULL)
  4470. || pfsPlayer->GetIGCShip()->IsGhost())
  4471. break;
  4472. IsideIGC* pside = pfsPlayer->GetSide();
  4473. if (pside->GetObjectID() < 0)
  4474. break;
  4475. TrapHackBoot(pfsPlayer->GetIGCShip()->GetParentShip() == NULL);
  4476. CASTPFM(pfmBuyLoadout, C, BUY_LOADOUT, pfm);
  4477. TrapHackBoot(pfmBuyLoadout->ibloadout == sizeof(FMD_C_BUY_LOADOUT));
  4478. TrapHackBoot(pfmBuyLoadout->cbloadout >= sizeof(ShipLoadout));
  4479. TrapHackBoot((pfmBuyLoadout->cbloadout - sizeof(ShipLoadout)) % sizeof(ExpandedPartData) >= 0);
  4480. TrapHackBoot(pfmBuyLoadout->cbloadout <= c_cbLoadout);
  4481. // refund the cost for the player's current ship
  4482. Money cost = -pfsPlayer->GetIGCShip()->GetValue();
  4483. IhullTypeIGC* phtOld = pfsPlayer->GetIGCShip()->GetBaseHullType();
  4484. // buy what we can of the new ship
  4485. bool bBoughtEverything = pfsPlayer->GetIGCShip()->PurchaseShipLoadout(pfmBuyLoadout->cbloadout,
  4486. (const ShipLoadout*)(FM_VAR_REF(pfmBuyLoadout, loadout)));
  4487. // add up the cost of the new ship
  4488. cost += pfsPlayer->GetIGCShip()->GetValue();
  4489. // spend the money, if any
  4490. if (cost != 0)
  4491. {
  4492. TrapHackBoot(pfsPlayer->GetMoney() >= cost);
  4493. pfsPlayer->SetMoney(pfsPlayer->GetMoney() - cost);
  4494. //Debit the player's money to all other player's on the side
  4495. //(the player has already deducted the change and will ignore this)
  4496. BEGIN_PFM_CREATE(g.fm, pfmMoneyChange, S, MONEY_CHANGE)
  4497. END_PFM_CREATE
  4498. pfmMoneyChange->dMoney = -cost;
  4499. pfmMoneyChange->sidTo = pfsPlayer->GetShipID();
  4500. pfmMoneyChange->sidFrom = NA;
  4501. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  4502. }
  4503. //Boot all observers from the ship
  4504. const ShipListIGC* pshipsChildren = pfsPlayer->GetIGCShip()->GetChildShips();
  4505. IhullTypeIGC* phtNew = pfsPlayer->GetIGCShip()->GetBaseHullType();
  4506. if (phtOld != phtNew)
  4507. {
  4508. ShipLinkIGC* pslNext;
  4509. for (ShipLinkIGC* psl = pshipsChildren->first(); (psl != NULL); psl = pslNext)
  4510. {
  4511. pslNext = psl->next();
  4512. if (psl->data()->GetTurretID() < 0)
  4513. LeaveShip(((CFSShip*)(psl->data()->GetPrivateData()))->GetPlayer(), pfsPlayer->GetIGCShip());
  4514. }
  4515. }
  4516. bool bLaunch = pfmBuyLoadout->fLaunch && bBoughtEverything;
  4517. // tell the client what happened
  4518. BEGIN_PFM_CREATE(g.fm, pfmBuyLoadoutAck, S, BUY_LOADOUT_ACK)
  4519. FM_VAR_PARM(NULL, pfsPlayer->GetIGCShip()->ExportShipLoadout(NULL))
  4520. END_PFM_CREATE
  4521. pfsPlayer->GetIGCShip()->ExportShipLoadout((ShipLoadout*)(FM_VAR_REF(pfmBuyLoadoutAck, loadout)));
  4522. pfsPlayer->SaveDesiredLoadout();
  4523. pfmBuyLoadoutAck->fBoughtEverything = bBoughtEverything;
  4524. pfmBuyLoadoutAck->fLaunch = bLaunch;
  4525. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  4526. //Tell the surviving kids about dad's new toys
  4527. pfsPlayer->QueueLoadoutChange();
  4528. {
  4529. SideID sid = pfsPlayer->GetSide()->GetObjectID();
  4530. pfsPlayer->GetShipStatus(sid)->SetHullID(pfsPlayer->GetIGCShip()->GetBaseHullType()->GetObjectID());
  4531. for (ShipLinkIGC* psl = pshipsChildren->first(); (psl != NULL); psl = psl->next())
  4532. {
  4533. CFSShip* pfsChild = (CFSShip*)(psl->data()->GetPrivateData());
  4534. //Since it might have changed ... reset the stored turret ID as well
  4535. pfsChild->GetShipStatus(sid)->SetState(psl->data()->GetTurretID() == NA ? c_ssObserver : c_ssTurret);
  4536. g.fm.SendMessages(pfsChild->GetPlayer()->GetConnection(), FM_GUARANTEED, FM_DONT_FLUSH);
  4537. }
  4538. }
  4539. g.fm.PurgeOutBox();
  4540. if (bLaunch)
  4541. {
  4542. //Move the children of this ship to space as well
  4543. for (ShipLinkIGC* psl = pshipsChildren->first();
  4544. (psl != NULL);
  4545. psl = psl->next())
  4546. {
  4547. psl->data()->SetStation(NULL);
  4548. }
  4549. pfsPlayer->GetIGCShip()->SetStation(NULL);
  4550. }
  4551. }
  4552. break;
  4553. case FM_CS_DROP_PART:
  4554. {
  4555. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED) || (pfsPlayer->GetIGCShip()->GetSide()->GetObjectID() < 0))
  4556. break;
  4557. TrapHackBoot (pfsPlayer->GetIGCShip()->GetParentShip() == NULL);
  4558. IclusterIGC* pcluster = pfsPlayer->GetIGCShip()->GetCluster();
  4559. if (pcluster)
  4560. {
  4561. TrapHackBoot (pfsPlayer->GetSide()->GetObjectID() >= 0);
  4562. CASTPFM(pfmDropPart, CS, DROP_PART, pfm);
  4563. TrapHackBoot ((pfmDropPart->et >= NA) && (pfmDropPart->et < ET_MAX));
  4564. TrapHackBoot ((pfmDropPart->mount >= -c_maxCargo) && (pfmDropPart->mount < c_maxMountedWeapons));
  4565. TrapHackBoot ((pfmDropPart->mount <= 0) || (pfmDropPart->et == ET_Weapon));
  4566. TrapHackBoot ((pfmDropPart->mount < 0) || (pfmDropPart->et >= 0));
  4567. IpartIGC* ppart = pfsPlayer->GetIGCShip()->GetMountedPart(pfmDropPart->et, pfmDropPart->mount);
  4568. if (ppart)
  4569. {
  4570. pfmDropPart->shipID = pfsPlayer->GetShipID();
  4571. g.fm.ForwardMessage(GetGroupSectorFlying(pcluster), pfmDropPart, FM_GUARANTEED);
  4572. ObjectType type = ppart->GetObjectType();
  4573. if (((type != OT_pack) && !IlauncherIGC::IsLauncher(type)) ||
  4574. (ppart->GetAmount() != 0))
  4575. {
  4576. CreateTreasure(g.timeNow, pfsPlayer->GetIGCShip(), ppart, ppart->GetPartType(),
  4577. pfsPlayer->GetIGCShip()->GetPosition(), 25.0f, 120.0f);
  4578. }
  4579. ppart->Terminate();
  4580. }
  4581. }
  4582. }
  4583. break;
  4584. case FM_C_SUICIDE:
  4585. {
  4586. // Only allow if player can cheat or is a child ship
  4587. if (!(pfsPlayer->GetIGCShip()->GetParentShip() || pfsPlayer->CanCheat()))
  4588. break;
  4589. // Mission must be started
  4590. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED))
  4591. break;
  4592. //if the client does this multiple times, make sure we don't barf
  4593. if (pfsPlayer->GetIGCShip()->GetCluster()) // NOT the pfsPlayer's cluster
  4594. pfsMission->GetSite()->KillShipEvent(g.timeNow, pfsPlayer->GetIGCShip(), NULL, 1.0f,
  4595. pfsPlayer->GetIGCShip()->GetSourceShip()->GetPosition(), Vector::GetZero());
  4596. break;
  4597. }
  4598. case FM_C_PROMOTE:
  4599. {
  4600. // Mission must be started
  4601. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED) || pfsPlayer->GetIGCShip()->IsGhost())
  4602. break;
  4603. CASTPFM(pfmPromoteC, C, PROMOTE, pfm);
  4604. for (ShipLinkIGC* psl = pfsPlayer->GetIGCShip()->GetChildShips()->first(); (psl != NULL); psl = psl->next())
  4605. {
  4606. IshipIGC* pship = psl->data();
  4607. if (pship->GetTurretID() == pfmPromoteC->mountidPromoted)
  4608. {
  4609. pship->Promote();
  4610. ((CFSShip*)(pship->GetPrivateData()))->ShipStatusRecalculate();
  4611. BEGIN_PFM_CREATE(g.fm, pfmPromote, S, PROMOTE)
  4612. END_PFM_CREATE
  4613. pfmPromote->shipidPromoted = pship->GetObjectID();
  4614. IclusterIGC* pcluster = pfsPlayer->GetIGCShip()->GetCluster();
  4615. if (pcluster)
  4616. g.fm.SendMessages(GetGroupSectorFlying(pcluster), FM_GUARANTEED, FM_FLUSH); //GetGroupRealSides(),
  4617. else
  4618. g.fm.SendMessages(CFSSide::FromIGC(pfsPlayer->GetSide())->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  4619. break;
  4620. }
  4621. }
  4622. }
  4623. break;
  4624. case FM_C_BOARD_SHIP:
  4625. {
  4626. // Mission must be started
  4627. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED) || pfsPlayer->GetIGCShip()->IsGhost())
  4628. break;
  4629. CASTPFM(pfmBoardShip, C, BOARD_SHIP, pfm);
  4630. if (pfmBoardShip->sidParent == NA)
  4631. {
  4632. IshipIGC* pship = pfsPlayer->GetIGCShip()->GetParentShip();
  4633. if (pship)
  4634. {
  4635. if (LeaveShip(pfsPlayer, pship))
  4636. break;
  4637. }
  4638. }
  4639. else
  4640. {
  4641. IshipIGC* pship = pfsPlayer->GetIGCShip()->GetSide()->GetShip(pfmBoardShip->sidParent);
  4642. if (pship && !pship->IsGhost())
  4643. {
  4644. if (BoardShip(pfsPlayer, pship))
  4645. break;
  4646. }
  4647. }
  4648. BEGIN_PFM_CREATE(g.fm, pfmBoardNack, S, BOARD_NACK)
  4649. END_PFM_CREATE
  4650. pfmBoardNack->sidRequestedParent;
  4651. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  4652. }
  4653. break;
  4654. case FM_C_VIEW_CLUSTER:
  4655. {
  4656. if (pfsPlayer->GetIGCShip()->GetStation())
  4657. {
  4658. CASTPFM(pfmViewCluster, C, VIEW_CLUSTER, pfm);
  4659. IclusterIGC* pcluster;
  4660. if (pfmViewCluster->clusterID != NA)
  4661. {
  4662. pcluster = pMission->GetCluster(pfmViewCluster->clusterID);
  4663. TrapHackBoot(pcluster);
  4664. BEGIN_PFM_CREATE(g.fm, pfmVC, S, VIEW_CLUSTER)
  4665. END_PFM_CREATE
  4666. pfmVC->clusterID = pfmViewCluster->clusterID;
  4667. pfmVC->bUsePosition = false;
  4668. if (pfmViewCluster->otTarget != NA)
  4669. {
  4670. ImodelIGC* ptarget = pMission->GetModel(pfmViewCluster->otTarget, pfmViewCluster->oidTarget);
  4671. if (ptarget && (ptarget->GetCluster() == pcluster) && ptarget->SeenBySide(pfsPlayer->GetSide()))
  4672. {
  4673. pfmVC->bUsePosition = true;
  4674. pfmVC->position = ptarget->GetPosition();
  4675. }
  4676. }
  4677. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  4678. }
  4679. else
  4680. pcluster = NULL;
  4681. pfsPlayer->SetCluster(pcluster, true);
  4682. }
  4683. }
  4684. break;
  4685. case FM_CS_SWAP_PART:
  4686. {
  4687. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED) || (pfsPlayer->GetIGCShip()->GetSide()->GetObjectID() < 0))
  4688. break;
  4689. TrapHackBoot (pfsPlayer->GetIGCShip()->GetParentShip() == NULL);
  4690. IclusterIGC* pcluster = pfsPlayer->GetIGCShip()->GetCluster();
  4691. if (pcluster)
  4692. {
  4693. CASTPFM(pfmSwapPart, CS, SWAP_PART, pfm);
  4694. assert (pfsPlayer->GetIGCShip()->GetBaseHullType());
  4695. TrapHack (pfmSwapPart->mountNew >= -c_maxCargo);
  4696. TrapHackBoot ((pfmSwapPart->etOld >= NA) && (pfmSwapPart->etOld < ET_MAX));
  4697. TrapHackBoot ((pfmSwapPart->mountOld >= -c_maxCargo) && (pfmSwapPart->mountOld < c_maxMountedWeapons));
  4698. TrapHackBoot ((pfmSwapPart->mountOld <= 0) || (pfmSwapPart->etOld == ET_Weapon));
  4699. TrapHackBoot ((pfmSwapPart->mountOld < 0) || (pfmSwapPart->etOld >= 0));
  4700. TrapHackBoot ((pfmSwapPart->mountNew >= -c_maxCargo) && (pfmSwapPart->mountNew < c_maxMountedWeapons));
  4701. TrapHackBoot ((pfmSwapPart->mountNew <= 0) || (pfmSwapPart->etOld == ET_Weapon));
  4702. TrapHackBoot ((pfmSwapPart->mountNew < 0) || (pfmSwapPart->etOld >= 0));
  4703. IpartIGC* ppart = pfsPlayer->GetIGCShip()->GetMountedPart(pfmSwapPart->etOld, pfmSwapPart->mountOld);
  4704. if (ppart)
  4705. {
  4706. IpartIGC* ppartNew = pfsPlayer->GetIGCShip()->GetMountedPart(pfmSwapPart->etOld, pfmSwapPart->mountNew);
  4707. if (ppartNew)
  4708. {
  4709. ppart->SetMountID(c_mountNA);
  4710. ppartNew->SetMountID(pfmSwapPart->mountOld);
  4711. }
  4712. ppart->SetMountID(pfmSwapPart->mountNew);
  4713. pfmSwapPart->shipID = pfsPlayer->GetShipID();
  4714. g.fm.ForwardMessage(GetGroupSectorFlying(pcluster), pfmSwapPart, FM_GUARANTEED);
  4715. }
  4716. }
  4717. break;
  4718. }
  4719. case FM_CS_RELOAD:
  4720. {
  4721. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED) || (pfsPlayer->GetIGCShip()->GetSide()->GetObjectID() < 0))
  4722. break;
  4723. TrapHackBoot (pfsPlayer->GetIGCShip()->GetParentShip() == NULL);
  4724. CASTPFM(pfmReload, CS, RELOAD, pfm);
  4725. TrapHackBoot(pfmReload->ibrgReloads == sizeof(FMD_CS_RELOAD));
  4726. IclusterIGC* pcluster = pfsPlayer->GetIGCShip()->GetCluster();
  4727. if (pcluster && (pfsPlayer->GetIGCShip()->GetParts()->n() != 0)) //NYI: hack to handle the lifepod case ... need something more sophisticated
  4728. {
  4729. const PartListIGC * ppartlist = pfsPlayer->GetIGCShip()->GetParts();
  4730. ReloadData* prlNext = (ReloadData*)FM_VAR_REF(pfmReload, rgReloads);
  4731. int nReloads = pfmReload->cbrgReloads / sizeof(ReloadData);
  4732. TrapHackBoot((nReloads > 0) && (nReloads <= 5)); //fuel, ammo, missile, chaff, dispenser
  4733. ReloadData* prlStop = prlNext + nReloads;
  4734. bool bBoot = false;
  4735. while (prlNext < prlStop)
  4736. {
  4737. if ((prlNext->mount >= 0) || (prlNext->mount < -c_maxCargo))
  4738. {
  4739. bBoot = true;
  4740. break;
  4741. }
  4742. IpartIGC* ppart = pfsPlayer->GetIGCShip()->GetMountedPart(NA, prlNext->mount);
  4743. if (!ppart)
  4744. {
  4745. bBoot = true;
  4746. break;
  4747. }
  4748. ObjectType type = ppart->GetObjectType();
  4749. if (type == OT_pack)
  4750. {
  4751. IpackIGC* ppack = (IpackIGC*)ppart;
  4752. PackType packtype = ppack->GetPackType();
  4753. short amount = ppack->GetAmount();
  4754. if (prlNext->amountTransfered == NA)
  4755. {
  4756. ppack->Terminate();
  4757. }
  4758. else
  4759. {
  4760. if ((prlNext->amountTransfered < 0) || (prlNext->amountTransfered >= amount))
  4761. {
  4762. bBoot = true;
  4763. break;
  4764. }
  4765. ppack->SetAmount(amount - prlNext->amountTransfered);
  4766. amount = prlNext->amountTransfered;
  4767. }
  4768. if (packtype == c_packAmmo)
  4769. {
  4770. pfsPlayer->GetIGCShip()->SetAmmo(pfsPlayer->GetIGCShip()->GetAmmo() + amount);
  4771. //disable all mounted weapons that use ammo
  4772. Mount maxWeapons = pfsPlayer->GetIGCShip()->GetHullType()->GetMaxWeapons();
  4773. for (Mount i = 0; (i < maxWeapons); i++)
  4774. {
  4775. IweaponIGC* pw = (IweaponIGC*)(pfsPlayer->GetIGCShip()->GetMountedPart(ET_Weapon, i));
  4776. if (pw && (pw->GetAmmoPerShot() != 0))
  4777. pw->SetMountedFraction(0.0f);
  4778. }
  4779. }
  4780. else
  4781. {
  4782. assert (packtype == c_packFuel);
  4783. pfsPlayer->GetIGCShip()->SetFuel(pfsPlayer->GetIGCShip()->GetFuel() + float(amount));
  4784. IpartIGC* pa = pfsPlayer->GetIGCShip()->GetMountedPart(ET_Afterburner, 0);
  4785. if (pa)
  4786. pa->SetMountedFraction(0.0f);
  4787. }
  4788. }
  4789. else
  4790. {
  4791. if (!IlauncherIGC::IsLauncher(type))
  4792. {
  4793. bBoot = true;
  4794. break;
  4795. }
  4796. IlauncherIGC* plauncher = (IlauncherIGC*)ppart;
  4797. IlauncherIGC* plauncherMounted = (IlauncherIGC*)(pfsPlayer->GetIGCShip()->GetMountedPart(plauncher->GetEquipmentType(), 0));
  4798. if (prlNext->amountTransfered == NA)
  4799. {
  4800. if ((plauncherMounted != NULL) && (plauncherMounted->GetAmount() != 0))
  4801. {
  4802. bBoot = true;
  4803. break;
  4804. }
  4805. if (plauncherMounted)
  4806. plauncherMounted->Terminate();
  4807. plauncher->SetMountID(0);
  4808. }
  4809. else
  4810. {
  4811. short amount = plauncher->GetAmount();
  4812. if ((!plauncherMounted) ||
  4813. (prlNext->amountTransfered <= 0) ||
  4814. (prlNext->amountTransfered > amount))
  4815. {
  4816. bBoot = true;
  4817. break;
  4818. }
  4819. if (amount == prlNext->amountTransfered)
  4820. plauncher->Terminate();
  4821. else
  4822. plauncher->SetAmount(amount - prlNext->amountTransfered);
  4823. plauncherMounted->SetAmount(plauncherMounted->GetAmount() + prlNext->amountTransfered);
  4824. plauncherMounted->SetMountedFraction(0.0f);
  4825. plauncherMounted->ResetTimeLoaded();
  4826. }
  4827. }
  4828. prlNext++;
  4829. }
  4830. if (bBoot)
  4831. {
  4832. TrapHackBoot(!bBoot);
  4833. }
  4834. else
  4835. {
  4836. pfmReload->shipID = pfsPlayer->GetShipID();
  4837. g.fm.ForwardMessage(GetGroupSectorFlying(pcluster), pfmReload, FM_GUARANTEED);
  4838. }
  4839. }
  4840. }
  4841. break;
  4842. case FM_C_POSITIONREQ:
  4843. {
  4844. if (!pfsMission)
  4845. break;
  4846. CASTPFM(pfmPositionReq, C, POSITIONREQ, pfm);
  4847. SideID iSide = pfmPositionReq->iSide;
  4848. // Choose a side if NA was specified (Pigs-only)
  4849. TrapHackBoot(iSide >= SIDE_TEAMLOBBY && iSide < c_cSidesMax);
  4850. if (NA == iSide)
  4851. iSide = pfsMission->PickNewSide(pfsPlayer, false, 0);
  4852. IsideIGC* psideReq = pMission->GetSide(iSide);
  4853. if (psideReq)
  4854. pfsMission->RequestPosition(pfsPlayer, psideReq, false);
  4855. else
  4856. {
  4857. //deny the request: side no longer exists
  4858. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  4859. END_PFM_CREATE
  4860. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  4861. pfmDelPosReq->iSide = iSide;
  4862. pfmDelPosReq->reason = DPR_SideGone;
  4863. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  4864. break;
  4865. }
  4866. break;
  4867. }
  4868. case FM_C_POSITIONACK:
  4869. {
  4870. if (!pfsMission)
  4871. break;
  4872. CASTPFM(pfmPositionAck, C, POSITIONACK, pfm);
  4873. CFSShip * pfssAck = CFSShip::GetShipFromID(pfmPositionAck->shipID);
  4874. IsideIGC * pside = pfsPlayer->GetSide(); // leader's side
  4875. TrapHackBoot(pfmPositionAck->iSide >= 0 && pfmPositionAck->iSide < c_cSidesMax);
  4876. // better be on the same mission, on the same side, and be the team leader
  4877. // (but we can't trap hack it because they may have been booted or
  4878. // demoted against their will)
  4879. if (pfmPositionAck->iSide != pside->GetObjectID()
  4880. || pfmPositionAck->iSide == SIDE_TEAMLOBBY
  4881. || pfsPlayer != pfsMission->GetLeader(pfmPositionAck->iSide))
  4882. break;
  4883. if (!pfssAck || pfssAck->GetMission() != pfsMission) // player already went away, so do nothing
  4884. break;
  4885. CFSPlayer * pfspAck = pfssAck->GetPlayer();
  4886. if (pfmPositionAck->fAccepted)
  4887. {
  4888. // if they still want the position...
  4889. if (pfsMission->CheckPositionRequest(pfssAck->GetPlayer(), pside) == NA
  4890. && pfsMission->RemoveJoinRequest(pfssAck->GetPlayer(), pside))
  4891. {
  4892. // put them on the side
  4893. pfsMission->AddPlayerToSide(pfspAck, pside);
  4894. }
  4895. }
  4896. else
  4897. {
  4898. pfsMission->RemoveJoinRequest(pfspAck, NULL);
  4899. }
  4900. break;
  4901. }
  4902. case FM_CS_QUIT_MISSION:
  4903. {
  4904. if (!pfsMission)
  4905. break;
  4906. IsideIGC* pside = pfsPlayer->GetSide(); // player's side
  4907. assert (pside);
  4908. SideID sideID = pside->GetObjectID();
  4909. if (sideID < 0 && sideID != SIDE_TEAMLOBBY)
  4910. break;
  4911. CASTPFM(pfmQuitMission, CS, QUIT_MISSION, pfm);
  4912. CFSShip * pfssAck = CFSShip::GetShipFromID(pfmQuitMission->shipID);
  4913. if (!pfssAck) // player already went away, so do nothing
  4914. break;
  4915. // the player must be the team leader or be booting themselves
  4916. // (but the client might mistakenly think it's the team leader)
  4917. if ((pfsPlayer == pfssAck) ||
  4918. ((pfsPlayer == pfsMission->GetLeader(sideID)) &&
  4919. (pfssAck->GetSide() == pside)))
  4920. {
  4921. CFSPlayer * pfspAck = pfssAck->GetPlayer();
  4922. bool bIsBoot = (pfsPlayer->GetShipID() != pfmQuitMission->shipID);
  4923. // make sure that they are requesting a reasonable reason.
  4924. TrapHackBoot(pfmQuitMission->reason == (bIsBoot ? QSR_LeaderBooted : QSR_Quit));
  4925. // send them to the game lobby
  4926. if (!bIsBoot || !pfsMission->GetMissionDef()->misparms.bLockTeamSettings)
  4927. pfsMission->RemovePlayerFromMission(pfspAck, pfmQuitMission->reason);
  4928. }
  4929. break;
  4930. }
  4931. case FM_CS_QUIT_SIDE:
  4932. {
  4933. if (!pfsMission)
  4934. break;
  4935. IsideIGC* pside = pfsPlayer->GetSide(); // player's side
  4936. assert (pside);
  4937. SideID sideID = pside->GetObjectID();
  4938. if (sideID < 0)
  4939. break;
  4940. CASTPFM(pfmQuitSide, CS, QUIT_SIDE, pfm);
  4941. CFSShip * pfssAck = CFSShip::GetShipFromID(pfmQuitSide->shipID);
  4942. if (!pfssAck) // player already went away, so do nothing
  4943. break;
  4944. TrapHackBoot(pfssAck->IsPlayer());
  4945. // the player must be the team leader or be booting themselves
  4946. // (but the client might mistakenly think it's the team leader)
  4947. if ((pfsPlayer == pfssAck) ||
  4948. ((pfsPlayer == pfsMission->GetLeader(sideID)) &&
  4949. (pfssAck->GetSide() == pside)))
  4950. {
  4951. CFSPlayer * pfspAck = pfssAck->GetPlayer();
  4952. bool bIsBoot = (pfsPlayer->GetShipID() != pfmQuitSide->shipID);
  4953. // make sure that they are requesting a reasonable reason.
  4954. TrapHackBoot(pfmQuitSide->reason == (bIsBoot ? QSR_LeaderBooted : QSR_Quit));
  4955. if (!bIsBoot && pfsMission->GetMissionDef()->misparms.bLockSides)
  4956. {
  4957. // turn it down because the sides are locked
  4958. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  4959. END_PFM_CREATE
  4960. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  4961. pfmDelPosReq->iSide = sideID;
  4962. pfmDelPosReq->reason = DPR_SidesLocked;
  4963. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  4964. }
  4965. else
  4966. {
  4967. // send them to the game lobby
  4968. if (!bIsBoot || !pfsMission->GetMissionDef()->misparms.bLockTeamSettings)
  4969. {
  4970. pfsMission->RemovePlayerFromSide(pfspAck, pfmQuitSide->reason);
  4971. if (bIsBoot)
  4972. {
  4973. // if they are banned from all sides kick them out of the game
  4974. unsigned char bannedSideMask = pfspAck->GetBannedSideMask();
  4975. unsigned char legalSideMask = SideMask(pfsMission->GetCountSides()) - 1;
  4976. if ((bannedSideMask & legalSideMask) == legalSideMask)
  4977. pfsMission->RemovePlayerFromMission(pfspAck, pfmQuitSide->reason);
  4978. }
  4979. }
  4980. }
  4981. }
  4982. break;
  4983. }
  4984. case FM_CS_AUTO_ACCEPT:
  4985. {
  4986. if (!pfsMission)
  4987. break;
  4988. CASTPFM(pfmAutoAccept, CS, AUTO_ACCEPT, pfm);
  4989. SideID sid = pfsPlayer->GetSide()->GetObjectID();
  4990. if (sid == SIDE_TEAMLOBBY)
  4991. break;
  4992. TrapHackBoot(sid >= 0 && sid < c_cSidesMax);
  4993. // can't be sure that they were not booted or something similar
  4994. if (pfsPlayer != pfsMission->GetLeader(sid))
  4995. break;
  4996. TrapHackBoot(pfmAutoAccept->iSide == sid);
  4997. // don't accept this message if the server is locked open
  4998. if (pfsMission->GetMissionDef()->misparms.bLockGameOpen)
  4999. break;
  5000. g.fm.ForwardMessage(pfsMission->GetGroupMission(), pfmAutoAccept, FM_GUARANTEED);
  5001. IsideIGC * pside = pMission->GetSide(pfmAutoAccept->iSide);
  5002. pfsMission->SetAutoAccept(pside, pfmAutoAccept->fAutoAccept);
  5003. break;
  5004. }
  5005. case FM_CS_LOCK_LOBBY:
  5006. {
  5007. if ((!pfsMission) || (pfsMission->GetOwner() != pfsPlayer))
  5008. break;
  5009. // don't accept this message if the server is locked open
  5010. if (pfsMission->GetMissionDef()->misparms.bLockGameOpen)
  5011. break;
  5012. CASTPFM(pfmLockLobby, CS, LOCK_LOBBY, pfm);
  5013. g.fm.ForwardMessage(pfsMission->GetGroupMission(), pfmLockLobby, FM_GUARANTEED);
  5014. pfsMission->SetLockLobby(pfmLockLobby->fLock);
  5015. break;
  5016. }
  5017. case FM_CS_LOCK_SIDES:
  5018. {
  5019. if ((!pfsMission) || (pfsMission->GetOwner() != pfsPlayer))
  5020. break;
  5021. CASTPFM(pfmLockSides, CS, LOCK_SIDES, pfm);
  5022. g.fm.ForwardMessage(pfsMission->GetGroupMission(), pfmLockSides, FM_GUARANTEED);
  5023. pfsMission->SetLockSides(pfmLockSides->fLock);
  5024. break;
  5025. }
  5026. case FM_C_RANDOMIZE_TEAMS:
  5027. {
  5028. if ((!pfsMission) || (pfsMission->GetOwner() != pfsPlayer))
  5029. break;
  5030. if (pfsMission->GetStage() != STAGE_NOTSTARTED)
  5031. break;
  5032. // randomly assign everyone to a side
  5033. pfsMission->RandomizeSides();
  5034. break;
  5035. }
  5036. case FM_CS_PLAYER_READY:
  5037. case FM_CS_FORCE_TEAM_READY:
  5038. {
  5039. if (!pfsMission)
  5040. break;
  5041. bool fIsForce = FM_CS_FORCE_TEAM_READY == pfm->fmid;
  5042. IsideIGC * pside = pfsPlayer->GetSide();
  5043. int iSide = pside->GetObjectID();
  5044. bool fReady = false;
  5045. bool fForceReady = false;
  5046. FEDMESSAGE * pfmForward = NULL;
  5047. if (fIsForce)
  5048. {
  5049. if (pfsPlayer != pfsMission->GetLeader(iSide))
  5050. break;
  5051. CASTPFM(pfmForceReady, CS, FORCE_TEAM_READY, pfm);
  5052. TrapHackBoot(iSide >= 0 && iSide < c_cSidesMax);
  5053. TrapHackBoot(pfmForceReady->iSide == iSide);
  5054. // tell everyone on the team about the ready status change
  5055. // we should do this b4 continuing, since the game might start, and they should get the ready change 1st
  5056. g.fm.ForwardMessage(pfsMission->GetGroupMission(), pfm, FM_GUARANTEED);
  5057. pfsMission->SetForceReady(iSide, pfmForceReady->fForceReady);
  5058. pfmForward = (FEDMESSAGE *) pfmForceReady;
  5059. }
  5060. else // regular player ready
  5061. {
  5062. CASTPFM(pfmPlayerReady, CS, PLAYER_READY, pfm);
  5063. TrapHackBoot(pfmPlayerReady->shipID == pfsPlayer->GetShipID());
  5064. fReady = pfmPlayerReady->fReady;
  5065. pfsPlayer->SetReady(pfmPlayerReady->fReady);
  5066. pfmForward = (FEDMESSAGE *) pfmPlayerReady;
  5067. g.fm.ForwardMessage(pfsMission->GetGroupMission(), pfm, FM_GUARANTEED);
  5068. }
  5069. break;
  5070. }
  5071. case FM_C_DOCKED:
  5072. {
  5073. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED))
  5074. break;
  5075. IsideIGC* pside = pfsPlayer->GetSide();
  5076. if (pside->GetObjectID() < 0)
  5077. break;
  5078. if (pfsPlayer->GetIGCShip()->GetStation() != NULL)
  5079. {
  5080. CASTPFM(pfmDocked, C, DOCKED, pfm);
  5081. IshipIGC* pshipParent = pfsPlayer->GetIGCShip()->GetParentShip();
  5082. if (pshipParent == NULL)
  5083. {
  5084. const ShipListIGC* pshipsChildren = pfsPlayer->GetIGCShip()->GetChildShips();
  5085. IstationIGC* pstation;
  5086. if ((pfmDocked->stationID == NA) && !pfsPlayer->GetIGCShip()->IsGhost())
  5087. {
  5088. pstation = NULL;
  5089. }
  5090. else
  5091. {
  5092. pstation = pfsMission->GetIGCMission()->GetStation(pfmDocked->stationID);
  5093. BEGIN_PFM_CREATE(g.fm, pfmAck, S, TELEPORT_ACK)
  5094. END_PFM_CREATE
  5095. if ((pstation == NULL) ||
  5096. (pstation->GetSide() != pside) ||
  5097. (!pstation->GetStationType()->HasCapability(c_sabmRestart)))
  5098. {
  5099. //Not a legitimate station to start at ... ignore the request
  5100. pfmAck->stationID = NA;
  5101. pfmAck->bNewHull = false;
  5102. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  5103. break;
  5104. }
  5105. pfmAck->stationID = pfmDocked->stationID;
  5106. pfmAck->bNewHull = !pstation->CanBuy(pfsPlayer->GetIGCShip()->GetBaseHullType());
  5107. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  5108. if (pfmAck->bNewHull)
  5109. {
  5110. //Switch to a lifepod
  5111. {
  5112. //Boot any kids
  5113. ShipLinkIGC* psl;
  5114. while (psl = pshipsChildren->first()) //intentional
  5115. LeaveShip((CFSShip*)(psl->data()->GetPrivateData()), pfsPlayer->GetIGCShip());
  5116. }
  5117. //Credit the player with the appropriate money
  5118. Money refund = pfsPlayer->GetIGCShip()->GetValue();
  5119. assert(refund >= 0);
  5120. if (refund > 0)
  5121. {
  5122. BEGIN_PFM_CREATE(g.fm, pfmMoney, S, MONEY_CHANGE)
  5123. END_PFM_CREATE
  5124. pfmMoney->sidTo = pfsPlayer->GetShipID();
  5125. pfmMoney->sidFrom = NA;
  5126. pfmMoney->dMoney = refund;
  5127. pfsPlayer->SetMoney(pfsPlayer->GetMoney() + refund);
  5128. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  5129. }
  5130. //Sell off any parts
  5131. {
  5132. const PartListIGC* ppartsList = pfsPlayer->GetIGCShip()->GetParts();
  5133. PartLinkIGC* ppl;
  5134. while (ppl = ppartsList->first()) //Intentional
  5135. ppl->data()->Terminate();
  5136. }
  5137. //Switch to a lifepod
  5138. IhullTypeIGC* pht = pside->GetCivilization()->GetLifepod();
  5139. pfsPlayer->GetIGCShip()->SetBaseHullType(pht);
  5140. ShipStatus* pss = pfsPlayer->GetShipStatus(pside->GetObjectID());
  5141. pss->SetHullID(pht->GetObjectID());
  5142. }
  5143. }
  5144. //Move the children of this ship to the station as well
  5145. for (ShipLinkIGC* psl = pshipsChildren->first();
  5146. (psl != NULL);
  5147. psl = psl->next())
  5148. {
  5149. psl->data()->SetStation(pstation);
  5150. }
  5151. pfsPlayer->GetIGCShip()->SetStation(pstation);
  5152. }
  5153. }
  5154. }
  5155. break;
  5156. case FM_C_BUCKET_DONATE:
  5157. {
  5158. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED))
  5159. break;
  5160. CASTPFM(pfmBucketDonate, C, BUCKET_DONATE, pfm);
  5161. IsideIGC* pside = pfsPlayer->GetSide();
  5162. IbucketIGC* pbucket = pside->GetBucket(pfmBucketDonate->iBucket);
  5163. TrapHackBoot(pbucket);
  5164. TrapHackBoot(pbucket->GetGroupID() >= 0);
  5165. TrapHackBoot(pfsPlayer->GetMoney() >= pfmBucketDonate->moneyGiven);
  5166. TrapHackBoot(pfmBucketDonate->moneyGiven >= 0);
  5167. Money mnyRefund = pfmBucketDonate->moneyGiven;
  5168. if (pside->CanBuy(pbucket))
  5169. {
  5170. Money mnySpent = pbucket->AddMoney(pfmBucketDonate->moneyGiven);
  5171. assert(mnySpent <= mnyRefund); // ACTUAL $ spent must be no more than ATTEMPTED $ spent
  5172. mnyRefund -= mnySpent;
  5173. pfsPlayer->SetMoney(pfsPlayer->GetMoney() - mnySpent);
  5174. BEGIN_PFM_CREATE(g.fm, pfmMoneyChange, S, MONEY_CHANGE)
  5175. END_PFM_CREATE
  5176. pfmMoneyChange->dMoney = -mnySpent;
  5177. pfmMoneyChange->sidTo = pfsPlayer->GetShipID();
  5178. pfmMoneyChange->sidFrom = pfmMoneyChange->sidTo;
  5179. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  5180. if (pfsPlayer->GetMoney() < 0)
  5181. {
  5182. LPCSTR pszContext = pfsPlayer->GetIGCShip() ?
  5183. pfsPlayer->GetIGCShip()->GetMission()->GetContextName() : NULL;
  5184. _AGCModule.TriggerContextEvent(NULL, AllsrvEventID_MoneyError,
  5185. pszContext, pfsPlayer->GetName(), -1, -1, -1, 3,
  5186. "PlayerName", VT_LPSTR, pfsPlayer->GetName(),
  5187. "MoneyGiven", VT_I4, pfmBucketDonate->moneyGiven,
  5188. "MoneyLeft", VT_I4, pfsPlayer->GetMoney());
  5189. }
  5190. SideID sid = pside->GetObjectID();
  5191. BEGIN_PFM_CREATE(g.fm, pfmBucketStatus, S, BUCKET_STATUS)
  5192. END_PFM_CREATE
  5193. pfmBucketStatus->timeTotal = pbucket->GetTime();
  5194. pfmBucketStatus->moneyTotal = pbucket->GetMoney();
  5195. pfmBucketStatus->iBucket = pfmBucketDonate->iBucket;
  5196. pfmBucketStatus->sideID = sid;
  5197. //Everyone knowns about anyone's development of the game winning tech
  5198. IbuyableIGC* b = pbucket->GetBuyable();
  5199. CFMGroup * pgrp = ((b->GetObjectType() == OT_development) && (b->GetObjectID() == c_didTeamMoney)) ?
  5200. pfsMission->GetGroupMission() :
  5201. CFSSide::FromIGC(pside)->GetGroup();
  5202. g.fm.SendMessages(pgrp, FM_GUARANTEED, FM_FLUSH);
  5203. }
  5204. if (mnyRefund)
  5205. {
  5206. assert (mnyRefund > 0);
  5207. BEGIN_PFM_CREATE(g.fm, pfmMoneyChange, S, MONEY_CHANGE)
  5208. END_PFM_CREATE
  5209. pfmMoneyChange->dMoney = mnyRefund;
  5210. pfmMoneyChange->sidTo = pfsPlayer->GetShipID();
  5211. pfmMoneyChange->sidFrom = NA;
  5212. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  5213. }
  5214. break;
  5215. }
  5216. case FM_C_PLAYER_DONATE:
  5217. {
  5218. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED))
  5219. break;
  5220. CASTPFM(pfmPlayerDonate, C, PLAYER_DONATE, pfm);
  5221. TrapHackBoot(pfmPlayerDonate->moneyGiven > 0);
  5222. TrapHackBoot(pfsPlayer->GetMoney() >= pfmPlayerDonate->moneyGiven);
  5223. CFSShip * pfssTo = CFSShip::GetShipFromID(pfmPlayerDonate->shipID);
  5224. CFMRecipient * prcp = NULL;
  5225. if (pfmPlayerDonate->moneyGiven > 0)
  5226. {
  5227. BEGIN_PFM_CREATE(g.fm, pfmMoneyChange, S, MONEY_CHANGE)
  5228. END_PFM_CREATE
  5229. pfmMoneyChange->dMoney = pfmPlayerDonate->moneyGiven;
  5230. if (pfssTo && (pfsPlayer->GetSide() == pfssTo->GetSide()) && (pfsPlayer->GetMoney() >= pfmPlayerDonate->moneyGiven))
  5231. {
  5232. pfmMoneyChange->sidTo = pfmPlayerDonate->shipID;
  5233. pfmMoneyChange->sidFrom = pfsPlayer->GetShipID();
  5234. pfsPlayer->SetMoney(pfsPlayer->GetMoney() - pfmMoneyChange->dMoney);
  5235. pfssTo->SetMoney(pfssTo->GetMoney() + pfmMoneyChange->dMoney);
  5236. prcp = CFSSide::FromIGC(pfsPlayer->GetSide())->GetGroup();
  5237. }
  5238. else
  5239. {
  5240. debugf("%s tried to donate money to ship %d, which doesn't exist in mission.\n",
  5241. pfsPlayer->GetName(), pfmPlayerDonate->shipID);
  5242. assert (pfmPlayerDonate->moneyGiven > 0);
  5243. pfmMoneyChange->sidTo = pfsPlayer->GetShipID();
  5244. pfmMoneyChange->sidFrom = NA;
  5245. prcp = pfsPlayer->GetConnection();
  5246. }
  5247. g.fm.SendMessages(prcp, FM_GUARANTEED, FM_FLUSH);
  5248. }
  5249. }
  5250. break;
  5251. case FM_CS_ORDER_CHANGE:
  5252. {
  5253. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED))
  5254. break;
  5255. CASTPFM(pfmOC, CS, ORDER_CHANGE, pfm);
  5256. IshipIGC* pship = pfsPlayer->GetIGCShip();
  5257. assert (pship);
  5258. IsideIGC* pside = pship->GetSide();
  5259. if (pside->GetObjectID() >= 0)
  5260. {
  5261. ImodelIGC* pmodel = pMission->GetModel(pfmOC->objectType, pfmOC->objectID);
  5262. pship->SetCommand(pfmOC->command, pmodel, pfmOC->commandID);
  5263. }
  5264. }
  5265. break;
  5266. case FM_CS_CHANGE_TEAM_CIV:
  5267. {
  5268. if (!pfsMission)
  5269. break;
  5270. IsideIGC * pside = pfsPlayer->GetSide();
  5271. SideID sid = pside->GetObjectID();
  5272. if (sid == SIDE_TEAMLOBBY || pfsPlayer != pfsMission->GetLeader(sid))
  5273. break;
  5274. CASTPFM(pfmChangeTeamCiv, CS, CHANGE_TEAM_CIV, pfm);
  5275. TrapHackBoot(NA != pfmChangeTeamCiv->civID);
  5276. TrapHackBoot(sid == pfmChangeTeamCiv->iSide);
  5277. if (pfsMission->GetStage() == STAGE_NOTSTARTED)
  5278. {
  5279. IcivilizationIGC * pciv = pMission->GetCivilization(pfmChangeTeamCiv->civID);
  5280. TrapHackBoot(pciv);
  5281. g.fm.ForwardMessage(pfsMission->GetGroupMission(), pfmChangeTeamCiv, FM_GUARANTEED);
  5282. pfsMission->SetSideCiv(pside, pciv);
  5283. }
  5284. }
  5285. break;
  5286. case FM_CS_SET_TEAM_INFO:
  5287. {
  5288. if (!pfsMission)
  5289. break;
  5290. if (pfsMission->GetStage() == STAGE_NOTSTARTED
  5291. && !pfsMission->GetMissionDef()->misparms.bLockTeamSettings)
  5292. {
  5293. CASTPFM(pfmSetTeamInfo, CS, SET_TEAM_INFO, pfm);
  5294. TrapHackBoot(pfmSetTeamInfo->sideID >= 0);
  5295. TrapHackBoot(pfmSetTeamInfo->sideID < c_cSidesMax);
  5296. TrapHackBoot(NULL != memchr(pfmSetTeamInfo->SideName, 0, c_cbName));
  5297. TrapHackBoot(pfsPlayer->GetIsMemberOfSquad(pfmSetTeamInfo->squadID));
  5298. // make sure we haven't run into any strange race conditions with team
  5299. // leadership.
  5300. if (pfmSetTeamInfo->sideID >= pfsMission->GetCountSides()
  5301. || pfsMission->GetLeader(pfmSetTeamInfo->sideID) != pfsPlayer
  5302. || pfsPlayer->GetSide()->GetObjectID() != pfmSetTeamInfo->sideID)
  5303. break;
  5304. if (pfsMission->GetMissionDef()->misparms.bSquadGame)
  5305. {
  5306. TrapHackBoot(pfsPlayer->GetCanLeadSquad(pfmSetTeamInfo->squadID)
  5307. || pfsPlayer->GetSide()->GetSquadID() == pfmSetTeamInfo->squadID);
  5308. if (pfmSetTeamInfo->squadID != NA)
  5309. pfsMission->SetSideSquad(pfmSetTeamInfo->sideID, pfmSetTeamInfo->squadID);
  5310. }
  5311. else
  5312. {
  5313. if (pfmSetTeamInfo->squadID == NA)
  5314. pfsMission->SetSideName(pfmSetTeamInfo->sideID, pfmSetTeamInfo->SideName);
  5315. }
  5316. }
  5317. }
  5318. break;
  5319. case FM_CS_DELPOSITIONREQ:
  5320. {
  5321. if (!pfsMission)
  5322. break;
  5323. CASTPFM(pfmDelPositionReq, CS, DELPOSITIONREQ, pfm);
  5324. ShipID shipID = pfsPlayer->GetShipID();
  5325. if (pfmDelPositionReq->shipID == shipID)
  5326. {
  5327. TrapHackBoot(pfmDelPositionReq->reason == DPR_Canceled);
  5328. }
  5329. else
  5330. {
  5331. TrapHackBoot(pfmDelPositionReq->reason == DPR_Rejected);
  5332. }
  5333. TrapHackBoot(pfmDelPositionReq->iSide >= 0 && pfmDelPositionReq->iSide < c_cSidesMax);
  5334. if (pfmDelPositionReq->iSide >= pfsMission->GetCountSides())
  5335. break;
  5336. if (pfmDelPositionReq->shipID != shipID
  5337. && pfsPlayer != pfsMission->GetLeader(pfmDelPositionReq->iSide))
  5338. break;
  5339. // tell everyone on the team about the change
  5340. g.fm.ForwardMessage(pfsMission->GetGroupMission(), pfm, FM_GUARANTEED);
  5341. // remove the request in question
  5342. pfsMission->RemoveJoinRequest(pfsPlayer, pfsMission->GetIGCMission()->GetSide(SIDE_TEAMLOBBY));
  5343. }
  5344. break;
  5345. case FM_CS_MISSIONPARAMS:
  5346. {
  5347. if (!pfsMission)
  5348. break;
  5349. CASTPFM(pfmMissionParams, CS, MISSIONPARAMS, pfm);
  5350. // the player better be the mission owner for this mission
  5351. if (pfsMission->GetOwner() != pfsPlayer)
  5352. break;
  5353. TrapHackBoot(pfmMissionParams->missionparams.Invalid(true) == NULL);
  5354. pfmMissionParams->missionparams.bObjectModelCreated =
  5355. !!pfsMission->GetMissionDef()->misparms.bObjectModelCreated;
  5356. #if !defined(ALLSRV_STANDALONE)
  5357. TrapHackBoot(pfmMissionParams->missionparams.bClubGame == true);
  5358. #else // !defined(ALLSRV_STANDALONE)
  5359. TrapHackBoot(pfmMissionParams->missionparams.bClubGame == false);
  5360. #endif // !defined(ALLSRV_STANDALONE)
  5361. // players can't change this stuff
  5362. TrapHackBoot(pfmMissionParams->missionparams.bLockGameOpen
  5363. == pfsMission->GetMissionDef()->misparms.bLockGameOpen);
  5364. TrapHackBoot(pfmMissionParams->missionparams.nTotalMaxPlayersPerGame
  5365. == pfsMission->GetMissionDef()->misparms.nTotalMaxPlayersPerGame);
  5366. if (pfsMission->GetMissionDef()->misparms.bLockGameOpen)
  5367. {
  5368. TrapHackBoot(
  5369. pfmMissionParams->missionparams.nMaxPlayersPerTeam
  5370. == pfsMission->GetMissionDef()->misparms.nMaxPlayersPerTeam
  5371. || pfmMissionParams->missionparams.nMaxPlayersPerTeam
  5372. >= pfmMissionParams->missionparams.nTotalMaxPlayersPerGame
  5373. / pfmMissionParams->missionparams.nTeams);
  5374. }
  5375. // if the mission has not started yet, change the settings
  5376. if (pfsMission->GetStage() == STAGE_NOTSTARTED)
  5377. {
  5378. #if defined(ALLSRV_STANDALONE)
  5379. // for a stand alone server, don't let the client try to change the password
  5380. // (we can't assert this due to a race condition)
  5381. strcpy(pfmMissionParams->missionparams.strGamePassword,
  5382. pfsMission->GetMissionDef()->misparms.strGamePassword);
  5383. strcpy(pfmMissionParams->missionparams.strGameName,
  5384. pfsMission->GetMissionDef()->misparms.strGameName);
  5385. #endif // defined(ALLSRV_STANDALONE)
  5386. // change the settings
  5387. pfsMission->SetMissionParams(pfmMissionParams->missionparams);
  5388. // tell the people in the game about the new settings...
  5389. g.fm.ForwardMessage(pfsMission->GetGroupMission(), pfsMission->GetMissionDef(), FM_GUARANTEED);
  5390. // and give the lobby a new abstract of the same
  5391. pfsMission->SetLobbyIsDirty();
  5392. LPCSTR pszContext = pfsMission->GetIGCMission()->GetContextName();
  5393. // Send an AGC event
  5394. _AGCModule.TriggerContextEvent(NULL, EventID_GameStateChange, pszContext,
  5395. pfsMission->GetMissionDef()->misparms.strGameName,
  5396. pfsMission->GetMissionID(), -1, -1, 0);
  5397. }
  5398. else
  5399. {
  5400. // otherwise silently ignore the change.
  5401. // REVIEW: do we need to try to make this race condition more visible?
  5402. }
  5403. }
  5404. break;
  5405. case FM_C_START_GAME:
  5406. {
  5407. // the player better be the mission owner for this mission
  5408. TrapHackBoot(pfsMission);
  5409. TrapHackBoot(pfsMission->GetOwner() == pfsPlayer);
  5410. // if the mission has not started yet but is ready to start, start the mission
  5411. if (pfsMission->GetStage() == STAGE_NOTSTARTED)
  5412. {
  5413. if (pfsMission->FAllReady())
  5414. {
  5415. // start the countdown
  5416. pfsMission->StartCountdown(c_fMissionBriefingCountdown);
  5417. }
  5418. }
  5419. }
  5420. break;
  5421. case FM_CS_SET_TEAM_LEADER:
  5422. {
  5423. if (!pfsMission)
  5424. break;
  5425. CASTPFM(pfmSetTeamLeader, CS, SET_TEAM_LEADER, pfm);
  5426. // the sender better be the current team leader
  5427. IsideIGC* psidePlayer = pfsPlayer->GetSide();
  5428. SideID sid = psidePlayer->GetObjectID();
  5429. TrapHackBoot(sid >= 0 && sid < c_cSidesMax);
  5430. if (pfsPlayer == pfsMission->GetLeader(sid))
  5431. {
  5432. TrapHackBoot(sid == pfmSetTeamLeader->sideID);
  5433. CFSShip* pfssNewLeader = CFSShip::GetShipFromID(pfmSetTeamLeader->shipID);
  5434. if (pfssNewLeader && pfssNewLeader->IsPlayer()
  5435. && pfssNewLeader->GetSide() == psidePlayer)
  5436. {
  5437. CFSPlayer * pfspNewLeader = pfssNewLeader->GetPlayer();
  5438. // if there is a human player still on their team with the given ship ID,
  5439. // make that new player the leader.
  5440. if (pfspNewLeader->GetSide() == psidePlayer)
  5441. pfsMission->SetLeader(pfspNewLeader);
  5442. }
  5443. }
  5444. }
  5445. break;
  5446. case FM_C_RIPCORD_REQUEST:
  5447. {
  5448. if (!pfsMission || (pfsMission->GetStage() != STAGE_STARTED) || (pfsPlayer->GetIGCShip()->GetSide()->GetObjectID() < 0) ||
  5449. pfsPlayer->GetIGCShip()->GetParentShip() ||
  5450. (pfsPlayer->GetIGCShip()->GetCluster() == NULL) ||
  5451. (pfsPlayer->GetIGCShip()->GetFlag() != NA) ||
  5452. pfsPlayer->GetIGCShip()->GetBaseHullType()->HasCapability(c_habmNoRipcord))
  5453. {
  5454. break;
  5455. }
  5456. CASTPFM(pfmRipcord, C, RIPCORD_REQUEST, pfm);
  5457. RequestRipcord(pfsPlayer->GetIGCShip(), pfsMission->GetIGCMission()->GetCluster(pfmRipcord->sidRipcord));
  5458. }
  5459. break;
  5460. default:
  5461. {
  5462. // an unrecognized message was received.
  5463. TrapHackBoot(false);
  5464. }
  5465. break;
  5466. } // switch(pfm->fmid)
  5467. // Don't let messages queue up cross incoming message boundaries
  5468. // If you're gonna queue a message, send it--don't make me guess who it goes to or whether it needs to be guaranteed.
  5469. assert(0 == g.fm.CbUsedSpaceInOutbox());
  5470. //g.fm.SetPriority(c_mcpDefault);
  5471. timerOnAppMessage.Stop("...for message type %s\n", g_rgszMsgNames[pfm->fmid]);
  5472. return(S_OK);
  5473. }
  5474. /*-------------------------------------------------------------------------
  5475. * CreateUniqueMutex
  5476. *-------------------------------------------------------------------------
  5477. * Purpose:
  5478. * Creates a mutex object, with a unique name, to indicate when the
  5479. * server is running.
  5480. *
  5481. */
  5482. HANDLE CreateUniqueMutex()
  5483. {
  5484. // Create a NULL dacl to give "everyone" access
  5485. SECURITY_ATTRIBUTES* psa = NULL;
  5486. SECURITY_DESCRIPTOR sd;
  5487. SECURITY_ATTRIBUTES sa = {sizeof(sa), &sd, false};
  5488. if (IsWinNT())
  5489. {
  5490. InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
  5491. SetSecurityDescriptorDacl(&sd, true, NULL, FALSE);
  5492. psa = &sa;
  5493. }
  5494. // Create the mutex using the global name first
  5495. HANDLE hMutex = ::CreateMutex(psa, false, szAllSrvRunningGlobal);
  5496. if (!hMutex)
  5497. hMutex = ::CreateMutex(psa, false, szAllSrvRunning);
  5498. return hMutex;
  5499. }
  5500. /*-------------------------------------------------------------------------
  5501. * CheckForNoShows
  5502. *-------------------------------------------------------------------------
  5503. Purpose:
  5504. We create the mission before anyone is actually here (as directed by the lobby)
  5505. But for whatever reason, if the person who requested the mission never shows up,
  5506. we want to make sure the missions gets killed, especially since we now don't
  5507. show the mission on the lobby for others to join until the creator gets in.
  5508. Side Effects:
  5509. Mission could be blown away
  5510. */
  5511. void CheckForNoShows()
  5512. {
  5513. #if !defined(ALLSRV_STANDALONE)
  5514. const ListFSMission * plistMission = CFSMission::GetMissions();
  5515. for (LinkFSMission * plinkMission = plistMission->first(); plinkMission; plinkMission = plinkMission->next())
  5516. {
  5517. CFSMission * pfsMission = plinkMission->data();
  5518. if (!pfsMission->GetMissionDef()->misparms.bObjectModelCreated && // Let admin games break the rules
  5519. pfsMission->GetCountOfPlayers(NULL, true) == 0)
  5520. {
  5521. // nuke it if it's been around more than 15 seconds
  5522. SYSTEMTIME stNow;
  5523. FILETIME ftNow;
  5524. FILETIME ftStart;
  5525. ::GetSystemTime(&stNow);
  5526. SystemTimeToFileTime(&stNow, &ftNow);
  5527. SystemTimeToFileTime(pfsMission->GetStartTime(), &ftStart);
  5528. ULONGLONG ullStart;
  5529. ULONGLONG ullNow;
  5530. CopyMemory(&ullStart, &ftStart, sizeof(ullStart));
  5531. CopyMemory(&ullNow, &ftNow, sizeof(ullNow));
  5532. if ((ULONG) (ullNow - ullStart) > 300000000) // 15 seconds, measaured in 100 nanoseconds
  5533. {
  5534. debugf("No one showed up for mission %x, killing it.\n", pfsMission->GetCookie());
  5535. delete pfsMission;
  5536. break; // deleting 1 mission per check is plenty
  5537. }
  5538. }
  5539. }
  5540. #endif
  5541. }
  5542. void DisconnectFromLobby()
  5543. {
  5544. // Disconnect from the lobby server
  5545. if (g.fmLobby.IsConnected())
  5546. {
  5547. _AGCModule.TriggerEvent(NULL, AllsrvEventID_DisconnectingLobby,
  5548. "", -1, -1, -1, 1,
  5549. "LobbyServer", VT_LPSTR, (LPCTSTR)g.strLobbyServer);
  5550. g.fmLobby.Shutdown();
  5551. _AGCModule.TriggerEvent(NULL, AllsrvEventID_DisconnectedLobby,
  5552. "", -1, -1, -1, 1,
  5553. "LobbyServer", VT_LPSTR, (LPCTSTR)g.strLobbyServer);
  5554. }
  5555. }
  5556. void ProcessNetworkMessages()
  5557. {
  5558. static CTimer timerReceiveMessages("in ReceiveMessages()", .1f);
  5559. static CTimer timerReceiveLobbyMessages("in lobby ReceiveMessages()", .1f);
  5560. static CTimer timerClientRollCall("doing client roll call", .1f);
  5561. // receive any messages in the queue
  5562. timerReceiveMessages.Start();
  5563. g.fm.ReceiveMessages();
  5564. timerReceiveMessages.Stop();
  5565. if (g.fmLobby.IsConnected())
  5566. {
  5567. timerReceiveLobbyMessages.Start();
  5568. g.fmLobby.ReceiveMessages();
  5569. timerReceiveLobbyMessages.Stop();
  5570. }
  5571. // Do a periodic roll call. If we haven't heard from anyone for two roll calls in a row, waste 'em
  5572. // Also waste 'em if they have more that 100 messages queued up (which is hopelessly behind)
  5573. static Time timeRollCall = Time::Now();
  5574. if (g.timeNow.clock() - timeRollCall.clock() > 5000) // 10 second interval
  5575. {
  5576. timerClientRollCall.Start();
  5577. ListConnections::Iterator iterCnxn(*g.fm.GetConnections());
  5578. while (!iterCnxn.End())
  5579. {
  5580. CFMConnection & cnxn = *iterCnxn.Value();
  5581. iterCnxn.Next(); // have to move iterator FIRST, because we might kill the current node
  5582. DWORD cMsgs;
  5583. DWORD cbMsgs;
  5584. HRESULT hr = g.fm.GetConnectionSendQueue(&cnxn, &cMsgs, &cbMsgs);
  5585. const cMaxQ = 50;
  5586. if ((cnxn.IncAbsentCount() > 6) || // give them a ridiculous 1 minute of no messages
  5587. FAILED(hr) ||
  5588. (cMsgs > cMaxQ)) // dead--nuke 'em
  5589. {
  5590. char szBuff[128];
  5591. int cb = wsprintf(szBuff, "Nuking %s(%u) because ", cnxn.GetName(), cnxn.GetID());
  5592. if (FAILED(hr))
  5593. {
  5594. wsprintf(szBuff + cb, "GetConnectionSendQueue returned 0x%08x.\n", hr);
  5595. debugf(szBuff);
  5596. }
  5597. else
  5598. {
  5599. DWORD dwHundredbpsG, dwmsLatencyG, dwHundredbpsU, dwmsLatencyU;
  5600. hr = g.fm.GetLinkDetails(&cnxn, &dwHundredbpsG, &dwmsLatencyG, &dwHundredbpsU, &dwmsLatencyU);
  5601. float kbpsG = (float) dwHundredbpsG / 10.0f;
  5602. float kbpsU = (float) dwHundredbpsU / 10.0f;
  5603. if (cMsgs > cMaxQ)
  5604. wsprintf(szBuff + cb, "they had too many messages (%u) queued up to them.\n", cMsgs);
  5605. else
  5606. wsprintf(szBuff + cb, "they missed roll call, with messages (%u) queued up to them.\n", cMsgs);
  5607. debugf(szBuff);
  5608. debugf("DPlay's view of the link was: %.1fkbps, %ums.\n", kbpsG, dwmsLatencyG);
  5609. }
  5610. float flTimeDiff = ((float) (timeRollCall.clock() - cnxn.GetLastComplete())) / 1000.0f;
  5611. debugf("The last completion was %.2f seconds ago.\n", flTimeDiff);
  5612. g.fm.DeleteConnection(cnxn);
  5613. }
  5614. }
  5615. timeRollCall = g.timeNow;
  5616. timerClientRollCall.Stop();
  5617. if (g.pServerCounters)
  5618. g.pServerCounters->timeNetworkMessages =
  5619. timerReceiveMessages.LastInterval() +
  5620. timerReceiveLobbyMessages.LastInterval() +
  5621. timerClientRollCall.LastInterval();
  5622. }
  5623. else if (g.pServerCounters)
  5624. g.pServerCounters->timeNetworkMessages = timerReceiveMessages.LastInterval() + timerReceiveLobbyMessages.LastInterval();
  5625. bool bSupposedToConnectToLobby;
  5626. bool bServerStartedByUI = false;
  5627. #if defined(ALLSRV_STANDALONE)
  5628. // If standalone, we download the cfg file right before connecting to the lobby
  5629. // if started by command-line, then always try to connect to lobby
  5630. // if not started by command-line (started by UI) then connect to lobby only for public games
  5631. bServerStartedByUI = _Module.WasCOMStarted();
  5632. bSupposedToConnectToLobby = !(FEDSRV_GUID != g.fm.GetHostApplicationGuid()); // public games
  5633. #else
  5634. bSupposedToConnectToLobby = !g.strLobbyServer.IsEmpty();
  5635. #endif
  5636. if (bSupposedToConnectToLobby) // are we supposed to be connected to a lobby?
  5637. {
  5638. // roll call for lobby
  5639. if (g.fmLobby.IsConnected())
  5640. {
  5641. static Time timeRollCallLobby = Time::Now();
  5642. if (g.timeNow.clock() - timeRollCallLobby.clock() > 4000) // 20s interval
  5643. {
  5644. BEGIN_PFM_CREATE(g.fmLobby, pfmHeartbeat, S, HEARTBEAT)
  5645. END_PFM_CREATE
  5646. g.fmLobby.SendMessages(g.fmLobby.GetServerConnection(), FM_GUARANTEED, FM_FLUSH);
  5647. timeRollCallLobby = g.timeNow;
  5648. CheckForNoShows(); // kinda of a weird place to be calling it, but since we're already in a perdiodic timer...
  5649. }
  5650. }
  5651. else // let's try to reconnect if things get slow (no running games)
  5652. {
  5653. static Time timeLastTry = Time::Now();
  5654. // retry once a minute. This will block for a few seconds, so we don't want to do it too often
  5655. if (g.timeNow.clock() - timeLastTry.clock() > 60000)
  5656. {
  5657. const ListFSMission * plistMission = CFSMission::GetMissions();
  5658. bool fAnyRunning = false;
  5659. for (LinkFSMission * plinkMission = plistMission->first(); !fAnyRunning && plinkMission; plinkMission = plinkMission->next())
  5660. {
  5661. CFSMission * pfsMission = plinkMission->data();
  5662. if (pfsMission->GetStage() == STAGE_STARTED)
  5663. fAnyRunning = true;
  5664. }
  5665. if (!fAnyRunning)
  5666. ConnectToLobby(NULL);
  5667. timeLastTry = g.timeNow;
  5668. }
  5669. }
  5670. }
  5671. }
  5672. void UpdateMissions(Time timeStart, Time timePrevious)
  5673. {
  5674. static CTimer timerIGC("updating all IGC missions", .1f);
  5675. timerIGC.Start();
  5676. static Time timLastStationUpdate(Time::Now());
  5677. const float dtimStationUpdateInterval = 0.75f;
  5678. const ListFSMission * plistMission = CFSMission::GetMissions();
  5679. LinkFSMission * plinkMissionNext;
  5680. for (LinkFSMission * plinkMission = plistMission->first(); plinkMission; plinkMission = plinkMissionNext)
  5681. {
  5682. plinkMissionNext = plinkMission->next();
  5683. CFSMission * pfsMission = plinkMission->data();
  5684. if (pfsMission->GetStage() < STAGE_OVER &&
  5685. pfsMission->GetIGCMission() != g.trekCore) // trekCore is a mission, but not a real one
  5686. {
  5687. // if the game is an auto-restart game and needs to have the countdown started, do so
  5688. float dtTimeTillStart = pfsMission->GetMissionDef()->misparms.timeStart - timeStart;
  5689. float dtPreviousTimeTillStart = pfsMission->GetMissionDef()->misparms.timeStart - timePrevious;
  5690. if (pfsMission->GetStage() == STAGE_NOTSTARTED)
  5691. {
  5692. if (pfsMission->GetMissionDef()->misparms.bAutoRestart)
  5693. {
  5694. if (dtTimeTillStart <= c_fMissionBriefingCountdown)
  5695. {
  5696. // arena style games don't have a minimum number of players per side
  5697. if (pfsMission->GetMissionDef()->misparms.bAllowEmptyTeams)
  5698. {
  5699. pfsMission->StartCountdown(max(0.0f, dtTimeTillStart));
  5700. }
  5701. else
  5702. {
  5703. // Force all sides to ready to prevent an AFK from canceling the game
  5704. for (SideID sideId = 0; sideId < pfsMission->GetMissionDef()->misparms.nTeams; sideId++)
  5705. pfsMission->SetForceReady(sideId, true);
  5706. if (pfsMission->FAllReady()) // a team was found to have less than the required players
  5707. pfsMission->StartCountdown(max(0.0f, dtTimeTillStart));
  5708. else
  5709. {
  5710. // let them completely run out of time before canceling the game
  5711. if (dtTimeTillStart <= 0)
  5712. {
  5713. pfsMission->SetStage(STAGE_OVER);
  5714. // Send chat to let everyone know the game is canceled
  5715. pfsMission->GetSite()->SendChat(NULL, CHAT_EVERYONE, NA, NA,
  5716. "Game canceled due to lack of players", c_cidNone, NA, NA, NULL, true);
  5717. }
  5718. }
  5719. }
  5720. }
  5721. else if (dtTimeTillStart <= 2 * c_fMissionBriefingCountdown
  5722. && dtPreviousTimeTillStart > 2 * c_fMissionBriefingCountdown)
  5723. {
  5724. // Send a warning chat to let everyone know the game is at risk of being canceled
  5725. if (!pfsMission->GetMissionDef()->misparms.bAllowEmptyTeams
  5726. && !pfsMission->FAllReady())
  5727. {
  5728. char szMessage[100];
  5729. sprintf(szMessage,
  5730. "Warning: Game will be canceled if all teams are not ready in %d seconds.",
  5731. int(2 * c_fMissionBriefingCountdown));
  5732. pfsMission->GetSite()->SendChat(NULL, CHAT_EVERYONE, NA, NA,
  5733. szMessage, c_cidNone, NA, NA, NULL, true);
  5734. }
  5735. }
  5736. }
  5737. else if (pfsMission->GetMissionDef()->misparms.bAutoStart && pfsMission->FAllReady())
  5738. {
  5739. pfsMission->StartCountdown(pfsMission->GetMissionDef()->misparms.fStartCountdown);
  5740. }
  5741. }
  5742. // if the countdown for a game has expired (and someone is in the game), start the game
  5743. if (pfsMission->GetStage() == STAGE_STARTING
  5744. && timeStart > pfsMission->GetMissionDef()->misparms.timeStart
  5745. && (pfsMission->HasPlayers(NULL, true)
  5746. || pfsMission->GetMissionDef()->misparms.bAllowEmptyTeams)
  5747. )
  5748. {
  5749. pfsMission->StartGame();
  5750. }
  5751. // Then do periodic updates
  5752. pfsMission->UpdateLobby(timeStart);
  5753. MoveShips(pfsMission, timeStart, timePrevious);
  5754. if (pfsMission->GetStage() == STAGE_STARTED)
  5755. {
  5756. if (timeStart - timLastStationUpdate > dtimStationUpdateInterval)
  5757. {
  5758. //Send station states to all of the docked players
  5759. for (ShipLinkIGC* psl = pfsMission->GetIGCMission()->GetShips()->first(); (psl != NULL); psl = psl->next())
  5760. {
  5761. IshipIGC* pship = psl->data();
  5762. IstationIGC* pstation = pship->GetStation();
  5763. if (pstation != NULL)
  5764. {
  5765. CFSShip* pfsship = (CFSShip*)(pship->GetPrivateData());
  5766. if (pfsship->IsPlayer())
  5767. {
  5768. // tell the client what happened
  5769. BEGIN_PFM_CREATE(g.fm, pfmStationsUpdate, S, STATIONS_UPDATE)
  5770. FM_VAR_PARM(NULL, sizeof(StationState))
  5771. END_PFM_CREATE
  5772. StationState* pss = (StationState*)(FM_VAR_REF(pfmStationsUpdate, rgStationStates));
  5773. pss->stationID = pstation->GetObjectID();
  5774. pss->bpHullFraction = pstation->GetFraction();
  5775. pss->bpShieldFraction = pstation->GetShieldFraction();
  5776. g.fm.SendMessages(pfsship->GetPlayer()->GetConnection(), FM_NOT_GUARANTEED, FM_FLUSH);
  5777. }
  5778. }
  5779. }
  5780. }
  5781. //Do economic stuff
  5782. pfsMission->DoTick(timeStart);
  5783. }
  5784. {
  5785. for (ShipLinkIGC* psl = pfsMission->GetIGCMission()->GetShips()->first(); (psl != NULL); psl = psl->next())
  5786. {
  5787. IshipIGC* pship = psl->data();
  5788. CFSShip* pfsship = (CFSShip*)(pship->GetPrivateData());
  5789. if (pfsship->IsPlayer())
  5790. pfsship->GetPlayer()->IncrementChatBudget();
  5791. }
  5792. }
  5793. }
  5794. if (pfsMission->ShouldDelete())
  5795. delete pfsMission;
  5796. }
  5797. timLastStationUpdate = timeStart;
  5798. timerIGC.Stop();
  5799. if (g.pServerCounters)
  5800. g.pServerCounters->timeIGCWork= timerIGC.LastInterval();
  5801. }
  5802. #if defined(ALLSRV_STANDALONE)
  5803. void GetExePath(char * szEXEPath)
  5804. {
  5805. ::GetModuleFileName(NULL, szEXEPath, MAX_PATH);
  5806. char* p = strrchr(szEXEPath, '\\');
  5807. if (p)
  5808. {
  5809. p++;
  5810. *p = 0; // erase filename
  5811. }
  5812. else
  5813. {
  5814. szEXEPath[0] = '\\';
  5815. szEXEPath[1] = '\0';
  5816. }
  5817. }
  5818. bool IsCFGFileValid(const char * szFileName)
  5819. {
  5820. const char * c_szValidCfg = "THIS IS A VALID CONFIG FILE";
  5821. ZFile file(szFileName);
  5822. int n = file.GetLength(); // -1 means error
  5823. if (n != -1 && n != 0)
  5824. {
  5825. char* pData = new char[n+1];
  5826. memcpy(pData, file.GetPointer(), n);
  5827. pData[n] = 0;
  5828. bool bValid = strstr(pData, c_szValidCfg) != NULL;
  5829. delete [] pData;
  5830. if (!bValid)
  5831. {
  5832. debugf("File %s is not a valid config file.\n", szFileName);
  5833. return false;
  5834. }
  5835. }
  5836. else
  5837. {
  5838. debugf("File %s error while trying to load downloaded config file.\n", szFileName);
  5839. return false;
  5840. }
  5841. return true;
  5842. }
  5843. IHTTPSession * BeginConfigDownload()
  5844. {
  5845. // Get the version information of the current module
  5846. ZVersionInfo vi;
  5847. ZString strBuild(vi.GetFileBuildNumber());
  5848. char szConfig[MAX_PATH];
  5849. lstrcpy(szConfig, "http://Allegiance.zone.com/Allegiance.cfg");
  5850. CRegKey key;
  5851. if (ERROR_SUCCESS == key.Open(HKEY_LOCAL_MACHINE, HKLM_FedSrv, KEY_READ))
  5852. {
  5853. ZString strConfig;
  5854. if (SUCCEEDED(LoadRegString(key, "cfgfile", strConfig)))
  5855. {
  5856. // if reg value exists copy over default
  5857. strcpy(szConfig, PCC(strConfig));
  5858. }
  5859. }
  5860. if (ZString(szConfig).Find("http://") == -1)
  5861. {
  5862. CopyFile(szConfig, ".\\temp.cfg", FALSE); // copy to current folder
  5863. debugf("Using local config file due to registry setting for ConfigFile because \"http://\" was missing from it. %s\n", szConfig);
  5864. return NULL; // true means already done
  5865. }
  5866. else
  5867. {
  5868. // Start the download of CFG file on secondary thread
  5869. IHTTPSession * pHTTPSession = CreateHTTPSession(NULL);
  5870. const char * szFileList[] = { szConfig, "temp.cfg", NULL };
  5871. pHTTPSession->InitiateDownload(szFileList, "."); // . means current path
  5872. return pHTTPSession;
  5873. }
  5874. }
  5875. // returns true only if up-to-date
  5876. bool OnAutoUpdateEXEDownloadDone(const char * szFilelistCRC, int nFilelistCRC, int nFilelistSize, const char * szSite, const char * szDirectory, int nAutoUpdateCRC, const char * szEXEPath)
  5877. {
  5878. char szPath[MAX_PATH + 16];
  5879. strcpy(szPath, szEXEPath);
  5880. strcat(szPath, "AutoUpdate.exe");
  5881. if (nAutoUpdateCRC != FileCRC(szPath, NULL))
  5882. {
  5883. // report CRC mismatch error for AutoUpdate.EXE
  5884. // todo fire event
  5885. char * szMsg = "ERROR: AutoUpdate was aborted. CRC for AutoUpdate.exe downloaded did not match CRC found in cfg file. You will not be able to host a public game.\n";
  5886. printf(szMsg);
  5887. MessageBox(NULL, szMsg, "Allegiance Server", MB_SERVICE_NOTIFICATION);
  5888. debugf("AutoUpdate.exe CRC mismatch\n");
  5889. return false;
  5890. }
  5891. //
  5892. // Decide what will be launched after the update (AllSrv32 or UI), and how it will be launched (as service or EXE)
  5893. //
  5894. char * szLaunchPostUpdate;
  5895. if (_Module.WasCOMStarted())
  5896. {
  5897. szLaunchPostUpdate = "AllSrvUI32.exe";
  5898. }
  5899. else
  5900. {
  5901. szLaunchPostUpdate = _Module.IsInstalledAsService() ? "S:AllSrv32" : "AllSrv32.exe"; // S: is short for "Start as Service"
  5902. }
  5903. char szCommandLine[MAX_PATH * 10];
  5904. _snprintf (szCommandLine, sizeof(szCommandLine)-1, "%s %s %d \"%s\" \"%s\" \"%s\"", szLaunchPostUpdate, szFilelistCRC, nFilelistSize, szSite, szDirectory, PCC(g.strArtPath));
  5905. printf("Launching AutoUpdate.exe ... ");
  5906. // Start the AutoUpdate utility
  5907. int nErr;
  5908. if((nErr = (int)ShellExecute(0,
  5909. "Open",
  5910. szPath,
  5911. szCommandLine,
  5912. NULL,
  5913. SW_SHOWNORMAL)
  5914. ) <= 32)
  5915. {
  5916. // report launch error
  5917. // todo fire event
  5918. printf("failed.\nError launching AutoUpdate.exe (Code = %d). You will not be able to host a public game.\n", nErr);
  5919. MessageBox(NULL, "Error launching AutoUpdate.exe. You will not be able to host a public game.", "Allegiance Server", MB_SERVICE_NOTIFICATION);
  5920. debugf("Couldn't launch AutoUpdate.exe (Code = %d)\n", nErr);
  5921. return false;
  5922. }
  5923. printf("succeeded.\n");
  5924. // Exit the application
  5925. PostQuitMessage(0); // allow proper shutdown to occur so Shutdown event gets fired to AllSRvUI32
  5926. //SetEvent(g.hKillReceiveEvent);
  5927. return false;
  5928. }
  5929. bool DownloadAutoUpdateEXE(const char * szFilelistCRC, int nFilelistCRC, int nFilelistSize, const char * szSite, const char * szDirectory, int nAutoUpdateCRC, const char * szAutoUpdateURL)
  5930. {
  5931. char szEXEPath[MAX_PATH];
  5932. GetExePath(szEXEPath);
  5933. char szPath[MAX_PATH];
  5934. strcpy(szPath, szEXEPath);
  5935. strcat(szPath, "AutoUpdate.exe");
  5936. printf("Server is out-of-date.\n");
  5937. if (nAutoUpdateCRC != FileCRC(szPath, NULL))
  5938. {
  5939. // Start the download of AutoUpdate.exe file
  5940. IHTTPSession * pHTTPSession = CreateHTTPSession(NULL);
  5941. const char * szFileList[] = { szAutoUpdateURL, "AutoUpdate.exe", NULL };
  5942. pHTTPSession->InitiateDownload(szFileList, szEXEPath);
  5943. debugf("Downloading AutoUpdate.exe file...");
  5944. printf("Downloading AutoUpdate.exe file ... ");
  5945. // we block as we download the AutoUpdate.exe file; should be okay since we are not connected to the zone.
  5946. while(pHTTPSession->ContinueDownload())
  5947. {
  5948. Sleep(100);
  5949. }
  5950. debugf("Done\n");
  5951. //
  5952. // At this point we are done downloading AutoUpdate.exe file
  5953. //
  5954. bool bErrorOccurred = pHTTPSession->GetLastErrorMessage() ? true : false;
  5955. char szErrorMsg[512];
  5956. if (bErrorOccurred)
  5957. _snprintf(szErrorMsg, sizeof(szErrorMsg), "\nError Connecting to Zone. You will not be able to host a public game. AutoUpdate.exe download failed. \r\n%s", pHTTPSession->GetLastErrorMessage());
  5958. delete pHTTPSession;
  5959. if (bErrorOccurred)
  5960. {
  5961. // report AutoUpdate.exe download error
  5962. // TODO: fire event
  5963. printf("failed: %s\n", szErrorMsg);
  5964. MessageBox(NULL, szErrorMsg, "Allegiance Server", MB_SERVICE_NOTIFICATION);
  5965. debugf("%s\n", szErrorMsg);
  5966. return false;
  5967. }
  5968. else
  5969. printf("succeeded.\n");
  5970. }
  5971. return OnAutoUpdateEXEDownloadDone(szFilelistCRC, nFilelistCRC, nFilelistSize, szSite, szDirectory, nAutoUpdateCRC, szEXEPath);
  5972. }
  5973. bool OnCFGDownloadDone(const char * szConfig) // returns true iff up-to-date
  5974. {
  5975. // Read the PublicLobby value
  5976. char szLobby[_MAX_PATH * 2];
  5977. GetPrivateProfileString("Allegiance", "PublicLobby", "",
  5978. szLobby, sizeofArray(szLobby), szConfig);
  5979. if (*szLobby == '\0')
  5980. return false;
  5981. g.strLobbyServer = szLobby;
  5982. char szFilelistCRC[50];
  5983. GetPrivateProfileString("AllSrvUI", "FilelistCRC", "0",
  5984. szFilelistCRC, sizeof(szFilelistCRC), szConfig);
  5985. _strupr(szFilelistCRC);
  5986. int nFilelistCRC = UTL::hextoi(szFilelistCRC);
  5987. // If the CRC is zero (or doesn't exist) just accept it
  5988. if (nFilelistCRC == 0)
  5989. return true;
  5990. char szEXEPath[MAX_PATH + 16];
  5991. GetExePath(szEXEPath);
  5992. char szPath[MAX_PATH + 16];
  5993. strcpy(szPath, szEXEPath);
  5994. strcat(szPath, "Filelist.txt");
  5995. // Ask if they want to auto update, if determined necessary
  5996. if (FileCRC(szPath, NULL) != nFilelistCRC)
  5997. {
  5998. char szStr[100];
  5999. GetPrivateProfileString("AllSrvUI", "FilelistSize", "0",
  6000. szStr, sizeof(szStr), szConfig);
  6001. int nFilelistSize = atoi(szStr);
  6002. // If the filelist size is zero (or doesn't exist) just accept it
  6003. if (nFilelistSize == 0)
  6004. return true;
  6005. char szSite[512];
  6006. GetPrivateProfileString("AllSrvUI", "Site", "",
  6007. szSite, sizeof(szSite), szConfig);
  6008. // If the site value is empty (or doesn't exist) just accept it
  6009. if (szSite[0] == '\0')
  6010. return true;
  6011. char szDirectory[512];
  6012. GetPrivateProfileString("AllSrvUI", "Directory", "",
  6013. szDirectory, sizeof(szDirectory), szConfig);
  6014. // If the directory value is empty (or doesn't exist) just accept it
  6015. if (szDirectory[0] == '\0')
  6016. return true;
  6017. char szAutoUpdateURL[512];
  6018. GetPrivateProfileString("AllSrvUI", "AutoUpdateURL", "",
  6019. szAutoUpdateURL, sizeof(szAutoUpdateURL), szConfig);
  6020. // If the URL value is empty (or doesn't exist) just accept it
  6021. if (szAutoUpdateURL[0] == '\0')
  6022. return true;
  6023. char szAutoUpdateCRC[30];
  6024. GetPrivateProfileString("AllSrvUI", "AutoUpdateCRC", "",
  6025. szAutoUpdateCRC, sizeof(szAutoUpdateCRC), szConfig);
  6026. _strupr(szAutoUpdateCRC);
  6027. int nAutoUpdateCRC = UTL::hextoi(szAutoUpdateCRC);
  6028. // If the AutoUpdate values are empty (or don't exist) just accept them
  6029. if (szAutoUpdateCRC[0] == '\0' || nAutoUpdateCRC == 0)
  6030. return true;
  6031. return DownloadAutoUpdateEXE(szFilelistCRC, nFilelistCRC, nFilelistSize, szSite, szDirectory, nAutoUpdateCRC, szAutoUpdateURL);
  6032. }
  6033. else
  6034. {
  6035. return true;
  6036. }
  6037. }
  6038. bool RetrieveCFGFile() // returns true iff cfg was successfully retrieved
  6039. {
  6040. IHTTPSession * pHTTPSession = BeginConfigDownload();
  6041. //
  6042. // At this point pHTTPSession is NULL iff we already have the cfg file (like for local cfg files)
  6043. //
  6044. if (pHTTPSession)
  6045. {
  6046. debugf("Downloading cfg file...");
  6047. // we block as we download the cfg file; should be okay since we are not connected to the zone.
  6048. while(pHTTPSession->ContinueDownload())
  6049. {
  6050. Sleep(100);
  6051. }
  6052. debugf("Done\n");
  6053. //
  6054. // At this point we are done downloading cfg file
  6055. //
  6056. bool bErrorOccurred = pHTTPSession->GetLastErrorMessage() ? true : false;
  6057. char szErrorMsg[512];
  6058. if (bErrorOccurred)
  6059. _snprintf(szErrorMsg, sizeof(szErrorMsg), "\nError Connecting to Zone. You will not be able to host a public game. \r\n%s", pHTTPSession->GetLastErrorMessage());
  6060. delete pHTTPSession;
  6061. if (bErrorOccurred)
  6062. {
  6063. // report cfg file download error
  6064. // TODO: fire event
  6065. printf("%s\n", szErrorMsg);
  6066. debugf("%s\n", szErrorMsg);
  6067. if (!_Module.IsInstalledAsService())
  6068. MessageBox(NULL, szErrorMsg, "Allegiance Server", MB_SERVICE_NOTIFICATION);
  6069. return false;
  6070. }
  6071. }
  6072. PathString pathCurrent(PathString::GetCurrentDirectory());
  6073. PathString pathConfig(pathCurrent + PathString(PathString("temp.cfg").GetFilename()));
  6074. if (IsCFGFileValid(PCC(pathConfig)))
  6075. {
  6076. return OnCFGDownloadDone(PCC(pathConfig));
  6077. }
  6078. else
  6079. {
  6080. // report error
  6081. // TODO: fire event
  6082. debugf("\nInvalid CFG File.\n");
  6083. char * szErrorMsg = "\nError Connecting to Zone. You will not be able to host a public game. (CFG File is invalid.)\n";
  6084. printf(szErrorMsg);
  6085. if (!_Module.IsInstalledAsService())
  6086. MessageBox(NULL, szErrorMsg, "Allegiance Server", MB_SERVICE_NOTIFICATION);
  6087. return false;
  6088. }
  6089. }
  6090. #endif // STANDALONE
  6091. /*-------------------------------------------------------------------------
  6092. * AnnouncePause
  6093. *-------------------------------------------------------------------------
  6094. Purpose:
  6095. Tell the lobby whether we're paused or not
  6096. */
  6097. void AnnouncePause()
  6098. {
  6099. BEGIN_PFM_CREATE(g.fmLobby, pfmPause, S, PAUSE)
  6100. END_PFM_CREATE
  6101. pfmPause->fPause = g.fPaused;
  6102. AGCEventID idEvent = g.fPaused ? AllsrvEventID_Pause : AllsrvEventID_Continue;
  6103. _AGCModule.TriggerEvent(NULL, idEvent, "", -1, -1, -1, 0);
  6104. char * szMode = g.fPaused ? "paused." : "continued.";
  6105. printf("Server %s.\n", szMode);
  6106. g.fmLobby.SendMessages(g.fmLobby.GetServerConnection(), FM_GUARANTEED, FM_FLUSH);
  6107. }
  6108. /*-------------------------------------------------------------------------
  6109. * RestartPausedMissions
  6110. *-------------------------------------------------------------------------
  6111. * Purpose:
  6112. * Restart any paused missions when the service is unpaused.
  6113. */
  6114. void RestartPausedMissions()
  6115. {
  6116. const ListFSMission * plistMission = CFSMission::GetMissions();
  6117. for (LinkFSMission * plinkMission = plistMission->first(); plinkMission; plinkMission = plinkMission->next())
  6118. {
  6119. CFSMission * pfsMission = plinkMission->data();
  6120. if (pfsMission->GetStage() == STAGE_OVER && pfsMission->GetMissionDef()->misparms.bAllowRestart)
  6121. pfsMission->SetStage(STAGE_NOTSTARTED);
  6122. }
  6123. }
  6124. HRESULT ConnectToLobby(const char * pszLobby_OverrideCFG /* NULL == do not override */)
  6125. {
  6126. // Disconnect from current lobby, if any
  6127. DisconnectFromLobby();
  6128. assert(!g.fmLobby.IsConnected());
  6129. // find out what lobby to use and/or autoupdate info
  6130. #if defined(ALLSRV_STANDALONE)
  6131. if (!RetrieveCFGFile())
  6132. return HRESULT_FROM_WIN32(ERROR_CRC);
  6133. #endif
  6134. if(pszLobby_OverrideCFG)
  6135. g.strLobbyServer = pszLobby_OverrideCFG;
  6136. printf("Connecting to lobby: %s ... ", PCC(g.strLobbyServer));
  6137. _AGCModule.TriggerEvent(NULL, AllsrvEventID_Connecting, "", -1, -1, -1, 1,
  6138. "LobbyServer", VT_LPSTR, PCC(g.strLobbyServer));
  6139. HRESULT hr = g.fmLobby.JoinSession(
  6140. #if defined(ALLSRV_STANDALONE)
  6141. FEDFREELOBBYSERVERS_GUID,
  6142. #else
  6143. FEDLOBBYSERVERS_GUID,
  6144. #endif
  6145. PCC(g.strLobbyServer), PCC(g.strLocalAddress));
  6146. if (FAILED(hr))
  6147. {
  6148. printf("failed.\n", PCC(g.strLobbyServer));
  6149. return hr;
  6150. }
  6151. printf("succeeded.\n", PCC(g.strLobbyServer));
  6152. // Save the new LobbyServer value
  6153. g.strLobbyServer = PCC(g.strLobbyServer);
  6154. _AGCModule.TriggerEvent(NULL, AllsrvEventID_ConnectedLobby,
  6155. "", -1, -1, -1, 1,
  6156. "LobbyServer", VT_LPSTR, PCC(g.strLobbyServer));
  6157. // Give lobby the version
  6158. BEGIN_PFM_CREATE(g.fmLobby, pfmLogon, S, LOGON_LOBBY)
  6159. END_PFM_CREATE
  6160. pfmLogon->verLobby = LOBBYVER_LS;
  6161. g.fmLobby.SendMessages(g.fmLobby.GetServerConnection(), FM_GUARANTEED, FM_FLUSH);
  6162. // Make sure the lobby knows whether we're paused before we do anything else
  6163. AnnouncePause();
  6164. // ok, now let's re-advertize our games
  6165. const ListFSMission * plistMission = CFSMission::GetMissions();
  6166. for (LinkFSMission * plinkMission = plistMission->first(); plinkMission; plinkMission = plinkMission->next())
  6167. {
  6168. CFSMission * pfsMission = plinkMission->data();
  6169. BEGIN_PFM_CREATE(g.fmLobby, pfmNewMission, S, NEW_MISSION)
  6170. END_PFM_CREATE
  6171. pfmNewMission->dwIGCMissionID = pfsMission->GetIGCMission()->GetMissionID();
  6172. // We DON'T have cookies for these anymore, so we don't want to be trying to use these w/ lobby until we get real cookie
  6173. pfsMission->SetCookie(0);
  6174. }
  6175. hr = g.fmLobby.SendMessages(g.fmLobby.GetServerConnection(), FM_GUARANTEED, FM_FLUSH);
  6176. //
  6177. // For standalone, create default games as needed
  6178. //
  6179. if (SUCCEEDED(hr))
  6180. {
  6181. #if defined(ALLSRV_STANDALONE)
  6182. // if we were started NOT from the COM object model, start any autostart games
  6183. if (!_Module.WasCOMStarted())
  6184. StartDefaultGames();
  6185. #endif
  6186. }
  6187. return hr;
  6188. }
  6189. /*-------------------------------------------------------------------------
  6190. * ProcessMsgPump
  6191. *-------------------------------------------------------------------------
  6192. Purpose:
  6193. Process Window messages while we wait for the specified amount of time or specified events
  6194. Parameters:
  6195. time to wait (ms)
  6196. count of events
  6197. array of events
  6198. Returns:
  6199. wait object signled (WAIT_OBJECT_0 + n), or WAIT_TIMEOUT
  6200. */
  6201. DWORD ProcessMsgPump(int dwSleep, DWORD nCount, LPHANDLE pHandles)
  6202. {
  6203. static bool s_fWinNT = ::IsWinNT();
  6204. Time timeBegin = Time::Now();
  6205. DWORD dwSleep1 = dwSleep;
  6206. DWORD dwEnd = timeBegin.clock() + dwSleep; // don't wanna use Time, because it forces use of floats
  6207. static CTimer timerMsgPump("in message pump", 0.25f);
  6208. static CTempTimer timerSingleMsg("handling single Windows msg", .01f);
  6209. timerMsgPump.Start();
  6210. DWORD dwWait = WAIT_TIMEOUT;
  6211. do
  6212. {
  6213. // Wait until either we get a message or we ran out of time
  6214. // You cannot run debug/test on Win95
  6215. #ifdef DEBUG
  6216. dwWait = MsgWaitForMultipleObjectsEx(nCount, pHandles, dwSleep, QS_ALLINPUT, MWMO_ALERTABLE);
  6217. #else
  6218. dwWait = MsgWaitForMultipleObjects(nCount, pHandles, false, dwSleep, QS_ALLINPUT);
  6219. #endif
  6220. if (WAIT_TIMEOUT == dwWait)
  6221. break;
  6222. // Process the message queue, if any messages were received
  6223. if ((WAIT_OBJECT_0 + nCount) == dwWait)
  6224. {
  6225. static MSG msg;
  6226. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  6227. {
  6228. // dispatch Windows Messages to allow for the admin tool's COM to work
  6229. timerSingleMsg.Start();
  6230. TranslateMessage(&msg);
  6231. switch (msg.message)
  6232. {
  6233. case wm_sql_querydone:
  6234. {
  6235. CSQLQuery * pQuery = (CSQLQuery *) msg.lParam;
  6236. pQuery->DataReady();
  6237. break;
  6238. }
  6239. case wm_fs_pause:
  6240. AnnouncePause();
  6241. if (!g.fPaused)
  6242. RestartPausedMissions();
  6243. break;
  6244. case WM_QUIT:
  6245. _Module.StopAllsrv();
  6246. break;
  6247. default:
  6248. DispatchMessage(&msg);
  6249. }
  6250. timerSingleMsg.Stop();
  6251. }
  6252. }
  6253. dwSleep = dwEnd - Time::Now().clock();
  6254. } while (dwSleep <= dwSleep1); //dwSleep can't be less than 0 (since it is a dword)
  6255. timerMsgPump.Stop();
  6256. if (g.pServerCounters)
  6257. g.pServerCounters->timeMsgPump= timerMsgPump.LastInterval();
  6258. return dwWait;
  6259. }
  6260. #if defined(ALLSRV_STANDALONE)
  6261. /*-------------------------------------------------------------------------
  6262. * StartDefaultGames
  6263. *-------------------------------------------------------------------------
  6264. * Purpose:
  6265. * Starts any games listed in the registry
  6266. */
  6267. void StartDefaultGames()
  6268. {
  6269. // if we happen to have games running, then no need to start the same games over again.
  6270. const ListFSMission * plistMission = CFSMission::GetMissions();
  6271. if(0 != plistMission->n())
  6272. return;
  6273. MissionParams mp;
  6274. mp.Reset();
  6275. // we access the registry here because these keys don't need to be saved as globals.
  6276. HKEY hKey;
  6277. DWORD dwType;
  6278. DWORD dwValue;
  6279. DWORD cbValue;
  6280. if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, HKLM_FedSrv, 0, KEY_READ, &hKey))
  6281. {
  6282. // get the max players per game to use.from the registry
  6283. cbValue = sizeof(dwValue);
  6284. if (ERROR_SUCCESS == ::RegQueryValueEx(hKey, "MaxPlayersPerGame", NULL, &dwType, (unsigned char*)&dwValue, &cbValue))
  6285. mp.nTotalMaxPlayersPerGame = min(c_cMaxPlayersPerGame, (short)dwValue);
  6286. else
  6287. mp.nTotalMaxPlayersPerGame = c_cMaxPlayersPerGame;
  6288. // lock the games open, if requested.
  6289. cbValue = sizeof(dwValue);
  6290. if (ERROR_SUCCESS == ::RegQueryValueEx(hKey, "LockOpen", NULL, &dwType, (unsigned char*)&dwValue, &cbValue))
  6291. mp.bLockGameOpen = !!dwValue;
  6292. mp.nMinPlayersPerTeam = 1;
  6293. mp.nMaxPlayersPerTeam = (char)(min(200, (int)mp.nTotalMaxPlayersPerGame / (int)mp.nTeams));
  6294. mp.bObjectModelCreated = true; // a little bit of a fib, but it makes it look normal
  6295. HRESULT hr = S_OK;
  6296. int iGame = 1;
  6297. while (SUCCEEDED(hr))
  6298. {
  6299. char cbValueName[24];
  6300. ZString strGameName;
  6301. // look for a registry key Game#
  6302. wsprintf(cbValueName, "Game%d", iGame);
  6303. hr = LoadRegString(hKey, cbValueName, strGameName);
  6304. if (SUCCEEDED(hr))
  6305. {
  6306. // copy the new name into the mission parameters
  6307. strncpy(mp.strGameName, strGameName, c_cbGameName);
  6308. mp.strGameName[c_cbGameName - 1] = '\0';
  6309. assert(!mp.Invalid());
  6310. printf("Starting game \"%s\"\n", mp.strGameName);
  6311. // Create the new mission
  6312. FedSrvSite * psiteFedSrv = new FedSrvSite();
  6313. CFSMission * pfsMissionNew = new CFSMission(mp, NULL, psiteFedSrv, psiteFedSrv, NULL, NULL);
  6314. // tell the lobby about the mission
  6315. if (g.fmLobby.IsConnected())
  6316. {
  6317. BEGIN_PFM_CREATE(g.fmLobby, pfmNewMission, S, NEW_MISSION)
  6318. END_PFM_CREATE
  6319. pfmNewMission->dwIGCMissionID = pfsMissionNew->GetIGCMission()->GetMissionID();
  6320. g.fmLobby.SendMessages(g.fmLobby.GetServerConnection(), FM_GUARANTEED, FM_FLUSH);
  6321. }
  6322. }
  6323. ++iGame;
  6324. }
  6325. RegCloseKey(hKey);
  6326. }
  6327. }
  6328. #endif
  6329. /*-------------------------------------------------------------------------
  6330. * ReceiveThread
  6331. *-------------------------------------------------------------------------
  6332. * Purpose:
  6333. * Receives messages for a mission, and does all periodic processing
  6334. *
  6335. * Parameters:
  6336. * pThreadParameter: Unused
  6337. */
  6338. DWORD WINAPI ReceiveThread(LPVOID pThreadParameter)
  6339. {
  6340. HANDLE rgeventHandles[2];
  6341. ZVersionInfo vi;
  6342. debugf("Running %s %s\n", (PCC) vi.GetInternalName(), (PCC) vi.GetProductVersionString());
  6343. debugf("Entering ReceiveThread\n");
  6344. DWORD dwUpdateInterval = g.nUpdatesPerSecond <= 0 ? (1000 / 10) : (1000 / g.nUpdatesPerSecond); // milliseconds
  6345. if (g.pServerCounters)
  6346. g.pServerCounters->timeCycleTarget = dwUpdateInterval;
  6347. printf("Preparing server...\n");
  6348. // Enter this thread into the COM STA
  6349. HRESULT hr = CoInitialize(NULL);
  6350. ZSucceeded(hr);
  6351. // Initialize the ATL module
  6352. _Module.Init(g.hInst); // init COM and/or NT service stuff
  6353. // Create the single instance of the CAdminServer object
  6354. CComObject<CAdminServer>* pServer = NULL;
  6355. ZSucceeded(pServer->CreateInstance(&pServer));
  6356. // Add the CAdminServer instance to the GIT (the only ref we keep)
  6357. ZSucceeded(GetAGCGlobal()->RegisterInterfaceInGlobal(pServer->GetUnknown(),
  6358. IID_IUnknown, &g.dwServerGITCookie));
  6359. // Create a uniquely-named mutex to indicate that the server is running
  6360. TCHandle shMutexRunning = CreateUniqueMutex();
  6361. // Register class objects
  6362. _Module.RegisterCOMObjects();
  6363. // Setup the database stuff and connect
  6364. #if !defined(ALLSRV_STANDALONE)
  6365. hr = g.sql.Init(g.strSQLConfig.m_str, g.idReceiveThread, g.csqlSilentThreads, g.csqlNotifyThreads);
  6366. if (FAILED(hr))
  6367. {
  6368. return hr;
  6369. }
  6370. #endif
  6371. UTL::SetArtPath(g.strArtPath);
  6372. g.trekCore = CreateMission(); // this isn't a *real* mission, just a gateway to static stuff
  6373. g.trekCore->Initialize (Time::Now(), &g.siteFedSrv);
  6374. // until the server loads the appropriate igc static file based on mission params
  6375. // and only has one game per exe
  6376. int iStaticCoreVersion;
  6377. #if !defined(ALLSRV_STANDALONE)
  6378. iStaticCoreVersion = LoadIGCStaticCore (IGC_ENCRYPT_CORE_FILENAME, g.trekCore, false, DoDecrypt);
  6379. #else // !defined(ALLSRV_STANDALONE)
  6380. iStaticCoreVersion = LoadIGCStaticCore (IGC_STATIC_CORE_FILENAME, g.trekCore, false);
  6381. #endif // !defined(ALLSRV_STANDALONE)
  6382. assert (iStaticCoreVersion != NA);
  6383. //
  6384. // This is the first place where we can might connect to a lobby
  6385. //
  6386. #if defined(ALLSRV_STANDALONE)
  6387. // standalone servers don't connect to lobby by default
  6388. if (_Module.WasCOMStarted())
  6389. {
  6390. pServer->put_PublicLobby(false); // false == private games mode
  6391. }
  6392. else
  6393. {
  6394. g.strLobbyServer="?"; // this will be overrided by cfg file download; needs to be non-blank so ConnectToLobby() is called
  6395. pServer->put_PublicLobby(true); // true means use public dplay session, and connect to lobby is g.strLobbyServer is not empty
  6396. }
  6397. #else
  6398. //non-standalone servers always should connect to a lobby
  6399. pServer->put_PublicLobby(true); // true means use public dplay session, and connect to lobby is g.strLobbyServer is not empty
  6400. #endif // !defined(ALLSRV_STANDALONE)
  6401. rgeventHandles[0] = g.hKillReceiveEvent;
  6402. // rgeventHandles[1] = g.hPlayerEvent;
  6403. #if !defined(ALLSRV_STANDALONE)
  6404. LoadRankInfo();
  6405. LoadStaticMapInfo();
  6406. #endif
  6407. CTimer timerReceiveIterations("between iterations in ReceiveThread loop", .25f);
  6408. printf("Ready for clients.\n");
  6409. // loop waiting for player events. If the kill event is signaled
  6410. // the thread will exit
  6411. timerReceiveIterations.Start();
  6412. Time timeStartIteration = Time::Now();
  6413. Time timeEndIteration = timeStartIteration;
  6414. DWORD dwWait = WAIT_TIMEOUT;
  6415. while (dwWait != WAIT_OBJECT_0)
  6416. {
  6417. timerReceiveIterations.Stop();
  6418. timerReceiveIterations.Start();
  6419. if (g.pServerCounters)
  6420. g.pServerCounters->timeBetweenInnerLoops = timerReceiveIterations.LastInterval();
  6421. timeEndIteration = timeStartIteration;
  6422. timeStartIteration = Time::Now();
  6423. g.timeNow = timeStartIteration;
  6424. ProcessNetworkMessages();
  6425. UpdateMissions(timeStartIteration, timeEndIteration);
  6426. //Sleep an amount of time appropriate to total time will be dwUpdateInterval
  6427. DWORD dwIteration = (Time::Now().clock() - timeStartIteration.clock());
  6428. DWORD dwSleep = dwIteration < dwUpdateInterval
  6429. ? (dwUpdateInterval - dwIteration)
  6430. : 0;
  6431. dwWait = ProcessMsgPump(dwSleep, 1, rgeventHandles);
  6432. }
  6433. HAGCLISTENERS hListeners = GetAGCGlobal()->EventListeners(
  6434. EventID_ServerShutdown, -1, -1, -1);
  6435. if (hListeners)
  6436. {
  6437. // Get the computer name
  6438. char szName[MAX_COMPUTERNAME_LENGTH + 1];
  6439. DWORD cchName = sizeofArray(szName);
  6440. GetComputerNameA(szName, &cchName);
  6441. _AGCModule.TriggerEvent(hListeners, EventID_ServerShutdown, "", -1, -1, -1, 1,
  6442. "Server", VT_LPSTR, szName);
  6443. }
  6444. printf("Shutting down server...\n");
  6445. // Suspend creation of any objects
  6446. CoSuspendClassObjects();
  6447. // Revoke class objects
  6448. _Module.RevokeCOMObjects();
  6449. // Close the uniquely-named mutex to indicate that the server is not running
  6450. shMutexRunning = NULL;
  6451. // Get the CAdminServer instance from the GIT (to keep a ref on it)
  6452. IUnknownPtr spunkServer;
  6453. ZSucceeded(GetAGCGlobal()->GetInterfaceFromGlobal(g.dwServerGITCookie,
  6454. IID_IUnknown, (void**)&spunkServer));
  6455. // Remove the CAdminServer instance from the GIT
  6456. ZSucceeded(GetAGCGlobal()->RevokeInterfaceFromGlobal(g.dwServerGITCookie));
  6457. // Disconnect any other external references to the CAdminServer instance
  6458. CoDisconnectObject(spunkServer, 0);
  6459. // Release the CAdminServer instance
  6460. spunkServer = NULL;
  6461. #if !defined(ALLSRV_STANDALONE)
  6462. UnloadRankInfo();
  6463. UnloadStaticMapInfo();
  6464. #endif
  6465. // destroy IGC stuff
  6466. UnloadDbCache();
  6467. // Terminate the ATL module
  6468. _Module.Term();
  6469. // Remove this thread from the COM STA
  6470. CoUninitialize();
  6471. ExitThread(0);
  6472. return 0;
  6473. }
  6474. /*-------------------------------------------------------------------------
  6475. * LoadSettings
  6476. *-------------------------------------------------------------------------
  6477. * Purpose:
  6478. * Load static server settings
  6479. */
  6480. void LoadSettings()
  6481. {
  6482. // Explicitly initialize the global data structure
  6483. g.Init();
  6484. // defaults for reg keys:
  6485. // fTimeout, 1 1-okay to timeout clients
  6486. // fWantInt3, 1 1-want to hit an Int3 on asserts
  6487. // nUpdatesPerSecond, 10 the number of updates-per-second
  6488. // Set default g.strArtPath to "Artwork" under the EXE directory
  6489. TCHAR szModule[_MAX_PATH];
  6490. TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR];
  6491. GetModuleFileName(NULL, szModule, sizeofArray(szModule));
  6492. _tsplitpath(szModule, szDrive, szDir, NULL, NULL);
  6493. _makepath(szModule, szDrive, szDir, TEXT("Artwork\\"), NULL);
  6494. g.strArtPath = szModule;
  6495. //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  6496. //
  6497. // If you update this section, please update srvconfig to allow support
  6498. // without resorting to regedit.
  6499. //
  6500. //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  6501. HKEY hKey;
  6502. DWORD dwType;
  6503. DWORD dwValue;
  6504. DWORD cbValue;
  6505. if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, HKLM_FedSrv, 0, KEY_READ, &hKey))
  6506. {
  6507. if (FAILED(LoadRegString(hKey, "AuthServer", g.strAuthServer)))
  6508. g.strAuthServer = "Pointweb01"; // default for all developers
  6509. dwValue=0;
  6510. cbValue = sizeof(dwValue);
  6511. if (ERROR_SUCCESS == ::RegQueryValueEx(hKey, "nUpdatesPerSecond", NULL, &dwType, (unsigned char*)&dwValue, &cbValue))
  6512. g.nUpdatesPerSecond = dwValue;
  6513. else
  6514. g.nUpdatesPerSecond = 10;
  6515. #ifdef _DEBUG
  6516. dwValue=0;
  6517. cbValue = sizeof(dwValue);
  6518. if (ERROR_SUCCESS != ::RegQueryValueEx(hKey, "nDebugMask", NULL, &dwType, (unsigned char*)&dwValue, &cbValue))
  6519. dwValue=FED_DEBUG_FILE;
  6520. g_app.SetDebug(dwValue);
  6521. #endif
  6522. // Standalone servers do not need a LobbyServer registry value because
  6523. // they always get it from the CFG file.
  6524. #if !defined(ALLSRV_STANDALONE)
  6525. LoadRegString(hKey, "LobbyServer", g.strLobbyServer);
  6526. #endif // !defined(ALLSRV_STANDALONE)
  6527. // Use winsock-provided address for clients, unless overridden (e.g. dual nic)
  6528. if (FAILED(LoadRegString(hKey, "LocalAddress", g.strLocalAddress)))
  6529. {
  6530. WSADATA WSAData;
  6531. char szHostName[20]; // length of machine name--also long enough for ip address if that's what we get
  6532. WSAStartup(MAKEWORD(1,1), &WSAData);
  6533. gethostname(szHostName, sizeof(szHostName));
  6534. WSACleanup();
  6535. g.strLocalAddress = szHostName;
  6536. }
  6537. ZString strArtPath;
  6538. if (SUCCEEDED(LoadRegString(hKey, "ArtPath", strArtPath)))
  6539. {
  6540. if (strArtPath.Right(1) != "\\")
  6541. strArtPath += "\\";
  6542. g.strArtPath = strArtPath;
  6543. }
  6544. dwValue=0;
  6545. cbValue = sizeof(dwValue);
  6546. if (ERROR_SUCCESS == ::RegQueryValueEx(hKey, "fWantInt3", NULL, &dwType, (unsigned char*)&dwValue, &cbValue))
  6547. g.fWantInt3 = !!dwValue;
  6548. else g.fWantInt3 = true;
  6549. dwValue=0;
  6550. cbValue = sizeof(dwValue);
  6551. if (ERROR_SUCCESS == ::RegQueryValueEx(hKey, "fProtocol", NULL, &dwType, (unsigned char*)&dwValue, &cbValue))
  6552. g.fProtocol = !!dwValue;
  6553. else g.fProtocol = true;
  6554. dwValue=0;
  6555. cbValue = sizeof(dwValue);
  6556. if (ERROR_SUCCESS == ::RegQueryValueEx(hKey, "fDoublePrecision", NULL, &dwType, (unsigned char*)&dwValue, &cbValue))
  6557. g.fDoublePrecision = !!dwValue;
  6558. else g.fDoublePrecision = true;
  6559. dwValue=0;
  6560. cbValue = sizeof(dwValue);
  6561. if (ERROR_SUCCESS == ::RegQueryValueEx(hKey, "fTimeout", NULL, &dwType, (unsigned char*)&dwValue, &cbValue))
  6562. g.fTimeOut = !!dwValue;
  6563. else g.fTimeOut = true;
  6564. #if !defined(ALLSRV_STANDALONE)
  6565. dwValue=0;
  6566. cbValue = sizeof(dwValue);
  6567. if (ERROR_SUCCESS == ::RegQueryValueEx(hKey, "SQLThreadsNotify", NULL, &dwType, (unsigned char*)&dwValue, &cbValue))
  6568. g.csqlNotifyThreads = dwValue;
  6569. else g.csqlNotifyThreads = 3;
  6570. dwValue=0;
  6571. cbValue = sizeof(dwValue);
  6572. if (ERROR_SUCCESS == ::RegQueryValueEx(hKey, "SQLThreadsSilent", NULL, &dwType, (unsigned char*)&dwValue, &cbValue))
  6573. g.csqlSilentThreads = dwValue;
  6574. else g.csqlSilentThreads = 2;
  6575. if (FAILED(LoadRegString(hKey, "SQLConfig", g.strSQLConfig)))
  6576. g.strSQLConfig.Empty(); // default for all developers
  6577. #endif // !defined(ALLSRV_STANDALONE)
  6578. ::RegCloseKey(hKey);
  6579. }
  6580. }
  6581. /*-------------------------------------------------------------------------
  6582. * StringStartsWith ()
  6583. *-------------------------------------------------------------------------
  6584. * Purpose:
  6585. * find out if a string starts with another string
  6586. *
  6587. * Parameters: string to search, and string to find
  6588. */
  6589. bool StringStartsWith (char* src, char* find)
  6590. {
  6591. int find_length = strlen (find);
  6592. for (int i = 0; i < find_length; i++)
  6593. if (tolower (src[i]) != tolower (find[i]))
  6594. return false;
  6595. return true;
  6596. }
  6597. /*-------------------------------------------------------------------------
  6598. * BuildIGCFiles ()
  6599. *-------------------------------------------------------------------------
  6600. * Purpose:
  6601. * Build IGC files from the database
  6602. *
  6603. * Parameters: none
  6604. */
  6605. void BuildIGCFiles (char* szArtworkPath, bool bEncrypt = false, bool bMaps = true)
  6606. {
  6607. #if !defined(ALLSRV_STANDALONE)
  6608. // standardize the format of the path string
  6609. int iPathLastChar = strlen (szArtworkPath) - 1;
  6610. if (szArtworkPath[iPathLastChar] == '\\')
  6611. szArtworkPath[iPathLastChar] = 0;
  6612. UTL::SetArtPath(szArtworkPath);
  6613. // load the static data and write it out
  6614. LoadDbCache();
  6615. printf (bEncrypt ? " Writing encrypt core..." : " Writing static core...");
  6616. // we always generate it as static_core, but then they can rename it if they want to create different cores
  6617. if (DumpIGCStaticCore (bEncrypt ? IGC_ENCRYPT_CORE_FILENAME : IGC_STATIC_CORE_FILENAME, g.trekCore, c_maskStaticTypes, bEncrypt ? DoEncrypt : NULL))
  6618. printf (" OK\n");
  6619. else
  6620. {
  6621. printf (" FAILED\n");
  6622. exit (1);
  6623. }
  6624. // There's a number of database sizes that are greater than the message sizes,
  6625. // and I don't want to put casts all over the place, so...
  6626. #pragma warning(push)
  6627. #pragma warning(disable : 4244)
  6628. if (bMaps)
  6629. {
  6630. // now loop over all of the map data...
  6631. SQL_GO(MapsCount);
  6632. SQL_GETROW(MapsCount);
  6633. MapDescription* pMaps = new MapDescription[g_cMapsCount];
  6634. MapFileSite* pSite = new MapFileSite;
  6635. SQL_GO(MapIDs);
  6636. for (int i = 0; i < g_cMapsCount; i++)
  6637. {
  6638. if (SQL_SUCCEEDED(SQL_GETROW(MapIDs)))
  6639. {
  6640. pMaps[i].mapID = MapIDs_MapID;
  6641. SQLSTRCPY (pMaps[i].szFileName, MapIDs_FileName);
  6642. }
  6643. }
  6644. for (i = 0; i < g_cMapsCount; i++)
  6645. {
  6646. if (StringStartsWith (pMaps[i].szFileName, "template") || StringStartsWith (pMaps[i].szFileName, "t_"))
  6647. continue;
  6648. printf (" Creating Map %s...\n", pMaps[i].szFileName);
  6649. ImissionIGC* pMission = CreateMission();
  6650. pMission->Initialize(Time::Now(), pSite);
  6651. int iStaticCoreVersion = LoadIGCStaticCore (bEncrypt ? IGC_ENCRYPT_CORE_FILENAME : IGC_STATIC_CORE_FILENAME, pMission, false, bEncrypt ? DoDecrypt : NULL);
  6652. assert (iStaticCoreVersion != NA);
  6653. SectorInfo_MapID = StationInstance_MapID = Asteroids_MapID = AlephInst_MapID = MineInstance_MapID = ProbeInstance_MapID = TreasureInstance_MapID = pMaps[i].mapID;
  6654. //-----------------------------------------------------------------
  6655. // clusters
  6656. //-----------------------------------------------------------------
  6657. printf (" --------------------------------------------------\n");
  6658. SQL_GO(SectorInfo);
  6659. while(SQL_SUCCEEDED(SQL_GETROW(SectorInfo)))
  6660. {
  6661. DataClusterIGC obj;
  6662. obj.starSeed = SectorInfo_starSeed;
  6663. obj.lightDirection = (Vector (SectorInfo_LightX, SectorInfo_LightY, SectorInfo_LightZ)).Normalize ();
  6664. obj.lightColor = SectorInfo_LightColor;
  6665. obj.screenX = SectorInfo_ScreenX;
  6666. obj.screenY = SectorInfo_ScreenY;
  6667. obj.clusterID = SectorInfo_SectorID;
  6668. obj.nDebris = SectorInfo_DebrisCount;
  6669. obj.nStars = SectorInfo_StarsCount;
  6670. SQLSTRCPY (obj.name, SectorInfo_Name);
  6671. SQLSTRCPY (obj.posterName, SectorInfo_PosterName);
  6672. SQLSTRCPY (obj.planetName, SectorInfo_PlanetName);
  6673. obj.planetSinLatitude.SetChar (SectorInfo_planetSinLatitude);
  6674. obj.planetLongitude.SetChar (SectorInfo_planetLongitude);
  6675. obj.planetRadius = SectorInfo_planetRadius;
  6676. obj.activeF = true;
  6677. obj.bHomeSector = SectorInfo_IsHomeSector ? true : false;
  6678. if (StringStartsWith (obj.name, "template") || StringStartsWith (obj.name, "t_"))
  6679. continue;
  6680. printf (" Creating Sector %s...\n", obj.name);
  6681. IObject* punk = pMission->CreateObject(g.timeNow, OT_cluster, &obj, sizeof(obj));
  6682. punk->Release();
  6683. }
  6684. //-----------------------------------------------------------------
  6685. // alephs
  6686. //-----------------------------------------------------------------
  6687. // count the number of alephs so we can preallocate them
  6688. printf (" --------------------------------------------------\n");
  6689. SQL_GO(AlephsCount);
  6690. SQL_GETROW(AlephsCount);
  6691. std::map<int, DataWarpIGC*> alephMap;
  6692. // walk through all the alephs in the database, storing each one into the map
  6693. SQL_GO(Alephs);
  6694. while(SQL_SUCCEEDED(SQL_GETROW(Alephs)))
  6695. {
  6696. DataWarpIGC* pObj = new DataWarpIGC;
  6697. pObj->warpDef.warpID = AlephInst_ID;
  6698. pObj->warpDef.destinationID = AlephInst_TargetAlephID;
  6699. pObj->warpDef.radius = AlephInst_Radius;
  6700. SQLSTRCPY (pObj->warpDef.textureName, AlephInst_Texture);
  6701. SQLSTRCPY (pObj->warpDef.iconName, AlephInst_PRIcon);
  6702. pObj->position = Vector (AlephInst_xLocation, AlephInst_yLocation, AlephInst_zLocation);
  6703. Vector forward (AlephInst_xForward, AlephInst_yForward, AlephInst_zForward);
  6704. forward.SetNormalize ();
  6705. pObj->forward = forward;
  6706. pObj->rotation = Rotation (forward, AlephInst_rRotation);
  6707. pObj->signature = AlephInst_Signature;
  6708. pObj->clusterID = AlephInst_SectorID;
  6709. alephMap[AlephInst_ID] = pObj;
  6710. }
  6711. // iterate over the alephs in the map, setting the names and creating the objects
  6712. std::map<int, DataWarpIGC*>::iterator alephMapIterator = alephMap.begin ();
  6713. while (alephMapIterator != alephMap.end ())
  6714. {
  6715. // get a pointer to the paired aleph
  6716. DataWarpIGC* pPairedAleph = alephMap[alephMapIterator->second->warpDef.destinationID];
  6717. // get the sector the paried aleph is in
  6718. IclusterIGC* pCluster = pMission->GetCluster (pPairedAleph->clusterID);
  6719. strcpy (alephMapIterator->second->name, pCluster->GetName ());
  6720. IObject* punk = pMission->CreateObject(g.timeNow, OT_warp, alephMapIterator->second, sizeof(DataWarpIGC));
  6721. punk->Release();
  6722. printf (" Creating Aleph from %s to %s...\n", pMission->GetCluster (alephMapIterator->second->clusterID)->GetName (), pCluster->GetName ());
  6723. alephMapIterator++;
  6724. }
  6725. // iterate over the alephs in the map deleting the pointers to DataWarpIGC structures
  6726. alephMapIterator = alephMap.begin ();
  6727. while (alephMapIterator != alephMap.end ())
  6728. {
  6729. delete alephMapIterator->second;
  6730. alephMapIterator++;
  6731. }
  6732. //-----------------------------------------------------------------
  6733. // asteroids
  6734. //-----------------------------------------------------------------
  6735. printf (" --------------------------------------------------\n");
  6736. SQL_GO(Asteroids);
  6737. while(SQL_SUCCEEDED(SQL_GETROW(Asteroids)))
  6738. {
  6739. DataAsteroidIGC obj;
  6740. // XXX TODO -> propogate this to the database
  6741. obj.signature = Asteroids_Signature;
  6742. obj.position = Vector (Asteroids_x, Asteroids_y, Asteroids_z);
  6743. Vector up,
  6744. forward,
  6745. axis;
  6746. if ((Asteroids_xUp == NA) && (Asteroids_yUp == NA) && (Asteroids_zUp == NA))
  6747. up = Vector::RandomDirection ();
  6748. else
  6749. up = (Vector (Asteroids_xUp, Asteroids_yUp, Asteroids_zUp)).Normalize ();
  6750. if ((Asteroids_xUp == NA) && (Asteroids_yUp == NA) && (Asteroids_zUp == NA))
  6751. forward = Vector::RandomDirection ();
  6752. else
  6753. forward = (Vector (Asteroids_xForward, Asteroids_yForward, Asteroids_zForward)).Normalize ();
  6754. if ((Asteroids_xRotation == NA) && (Asteroids_yRotation == NA) && (Asteroids_zRotation == NA))
  6755. {
  6756. axis = Vector::RandomDirection ();
  6757. if (Asteroids_rRotation == NA)
  6758. Asteroids_rRotation = random (0.0f, 1.0f) * pi;
  6759. }
  6760. else
  6761. axis = (Vector (Asteroids_xRotation, Asteroids_yRotation, Asteroids_zRotation)).Normalize ();
  6762. obj.up = up;
  6763. obj.forward = forward;
  6764. obj.rotation = Rotation (axis, Asteroids_rRotation);
  6765. obj.asteroidDef.ore = obj.asteroidDef.oreMax = Asteroids_Ore;
  6766. obj.asteroidDef.aabmCapabilities = Asteroids_Abilities;
  6767. obj.asteroidDef.asteroidID = Asteroids_AsteroidID;
  6768. obj.asteroidDef.hitpoints = Asteroids_HitPoints;
  6769. obj.asteroidDef.radius = Asteroids_Radius;
  6770. SQLSTRCPY (obj.asteroidDef.modelName, Asteroids_Model);
  6771. SQLSTRCPY (obj.asteroidDef.textureName, Asteroids_Texture);
  6772. SQLSTRCPY (obj.asteroidDef.iconName, Asteroids_PRIcon);
  6773. obj.clusterID = Asteroids_ClusterID;
  6774. SQLSTRCPY (obj.name, Asteroids_Name);
  6775. if (obj.name[0] == '#')
  6776. obj.name[0] = '\0';
  6777. obj.fraction = 1.0f;
  6778. if (StringStartsWith (obj.name, "template") || StringStartsWith (obj.name, "t_"))
  6779. continue;
  6780. printf (" Creating Asteroid %s...\n", obj.name);
  6781. IObject* punk = pMission->CreateObject(g.timeNow, OT_asteroid, &obj, sizeof(obj));
  6782. punk->Release();
  6783. }
  6784. //-----------------------------------------------------------------
  6785. // stations
  6786. //-----------------------------------------------------------------
  6787. // generate the sides
  6788. for (int j = 0; j < c_cSidesMax; j++)
  6789. {
  6790. DataSideIGC obj;
  6791. // XXX TODO -> propogate this to the database
  6792. obj.nFlags = 0;
  6793. obj.nArtifacts = 0;
  6794. obj.sideID = j;
  6795. obj.gasAttributes.Initialize();
  6796. obj.civilizationID = pMission->GetCivilizations()->first()->data()->GetObjectID();
  6797. obj.ttbmDevelopmentTechs.SetAll ();
  6798. obj.conquest = 0;
  6799. obj.territory = 0;
  6800. obj.nKills = obj.nDeaths = obj.nEjections = obj.nFlags = obj.nArtifacts
  6801. = obj.nBaseKills = obj.nBaseCaptures = 0;
  6802. obj.squadID = NA;
  6803. IObject* punk = pMission->CreateObject(g.timeNow, OT_side, &obj, sizeof(obj));
  6804. punk->Release();
  6805. }
  6806. // generate the actual stations
  6807. printf (" --------------------------------------------------\n");
  6808. SQL_GO(StationInstance);
  6809. while(SQL_SUCCEEDED(SQL_GETROW(StationInstance)))
  6810. {
  6811. DataStationIGC obj;
  6812. // XXX TODO -> propogate this to the database
  6813. obj.position = Vector (StationInstance_xLocation, StationInstance_yLocation, StationInstance_zLocation);
  6814. obj.up = (Vector (StationInstance_xUp, StationInstance_yUp, StationInstance_zUp)).Normalize ();
  6815. obj.forward = (Vector (StationInstance_xForward, StationInstance_yForward, StationInstance_zForward)).Normalize ();
  6816. obj.rotation = Rotation ((Vector (StationInstance_xRotation, StationInstance_yRotation, StationInstance_zRotation)).Normalize (), StationInstance_rRotation);
  6817. obj.clusterID = StationInstance_SectorID;
  6818. obj.sideID = StationInstance_SideID;
  6819. obj.stationID = StationInstance_StationID;
  6820. obj.stationTypeID = StationInstance_StationTypeID;
  6821. obj.bpHull = 1.0f;
  6822. obj.bpShield = 1.0f;
  6823. SQLSTRCPY (obj.name, StationInstance_Name);
  6824. if (StringStartsWith (obj.name, "template") || StringStartsWith (obj.name, "t_"))
  6825. continue;
  6826. printf (" Creating Station %s...\n", obj.name);
  6827. IObject* punk = pMission->CreateObject(g.timeNow, OT_station, &obj, sizeof(obj));
  6828. punk->Release();
  6829. }
  6830. //-----------------------------------------------------------------
  6831. // probes
  6832. //-----------------------------------------------------------------
  6833. printf (" --------------------------------------------------\n");
  6834. SQL_GO(ProbeInstance);
  6835. while(SQL_SUCCEEDED(SQL_GETROW(ProbeInstance)))
  6836. {
  6837. DataProbeExport obj;
  6838. obj.p0 = Vector (ProbeInstance_xLocation, ProbeInstance_yLocation, ProbeInstance_zLocation);
  6839. obj.time0 = 0;
  6840. obj.exportF = true;
  6841. obj.probeID = ProbeInstance_ProbeID;
  6842. obj.probetypeID = ProbeInstance_ProbeTypeID;
  6843. obj.clusterID = ProbeInstance_SectorID;
  6844. obj.sideID = ProbeInstance_SideID;
  6845. obj.shipID = NA;
  6846. obj.otTarget = NA;
  6847. obj.oidTarget = NA;
  6848. obj.createNow = true;
  6849. printf (" Creating probe %d...\n", ProbeInstance_ProbeID);
  6850. IprobeIGC* punk = static_cast<IprobeIGC*> (pMission->CreateObject (g.timeNow, OT_probe, &obj, sizeof(obj)));
  6851. punk->SetCreateNow ();
  6852. punk->Release();
  6853. }
  6854. //-----------------------------------------------------------------
  6855. // mines
  6856. //-----------------------------------------------------------------
  6857. printf (" --------------------------------------------------\n");
  6858. /*
  6859. struct DataMineBase
  6860. {
  6861. Vector p0;
  6862. Time time0;
  6863. MineID mineID;
  6864. bool exportF;
  6865. };
  6866. struct DataMineIGC : public DataMineBase
  6867. {
  6868. ImineTypeIGC* pminetype;
  6869. IshipIGC* pshipLauncher;
  6870. IsideIGC* psideLauncher;
  6871. IclusterIGC* pcluster;
  6872. };
  6873. struct DataMineExport : public DataMineBase
  6874. {
  6875. SectorID clusterID;
  6876. ExpendableTypeID minetypeID;
  6877. ShipID launcherID;
  6878. SideID sideID;
  6879. BytePercentage fraction;
  6880. bool createNow;
  6881. };
  6882. DEF_STMT(MineInstance, "SELECT "
  6883. "MineID, "
  6884. "MineTypeID, "
  6885. "SideID, SectorID, "
  6886. "LocationX, LocationY, LocationZ "
  6887. "FROM MineInstances "
  6888. "WHERE MapID = ?"
  6889. )
  6890. SQL_INT2_PARM(MineInstance_MineID, SQL_OUT_PARM)
  6891. SQL_INT2_PARM(MineInstance_MineTypeID, SQL_OUT_PARM)
  6892. SQL_INT1_PARM(MineInstance_SideID, SQL_OUT_PARM)
  6893. SQL_INT2_PARM(MineInstance_SectorID, SQL_OUT_PARM)
  6894. SQL_FLT4_PARM(MineInstance_xLocation, SQL_OUT_PARM)
  6895. SQL_FLT4_PARM(MineInstance_yLocation, SQL_OUT_PARM)
  6896. SQL_FLT4_PARM(MineInstance_zLocation, SQL_OUT_PARM)
  6897. SQL_INT2_PARM(MineInstance_MapID, SQL_IN_PARM)
  6898. */
  6899. SQL_GO(MineInstance);
  6900. while(SQL_SUCCEEDED(SQL_GETROW(MineInstance)))
  6901. {
  6902. DataMineExport obj;
  6903. obj.p0 = Vector (MineInstance_xLocation, MineInstance_yLocation, MineInstance_zLocation);
  6904. obj.time0 = 0;
  6905. obj.exportF = true;
  6906. obj.mineID = MineInstance_MineID;
  6907. obj.minetypeID = MineInstance_MineTypeID;
  6908. obj.launcherID = NA;
  6909. obj.clusterID = MineInstance_SectorID;
  6910. obj.sideID = MineInstance_SideID;
  6911. obj.fraction = 1.0f;
  6912. obj.createNow = true;
  6913. printf (" Creating mine %d...\n", MineInstance_MineID);
  6914. ImineIGC* punk = static_cast<ImineIGC*> (pMission->CreateObject (g.timeNow, OT_mine, &obj, sizeof(obj)));
  6915. punk->SetCreateNow ();
  6916. punk->Release();
  6917. }
  6918. //-----------------------------------------------------------------
  6919. // treasures
  6920. //-----------------------------------------------------------------
  6921. printf (" --------------------------------------------------\n");
  6922. /*
  6923. struct DataTreasureIGC
  6924. {
  6925. Vector p0;
  6926. Vector v0;
  6927. float lifespan;
  6928. Time time0;
  6929. ObjectID objectID;
  6930. TreasureID treasureID;
  6931. SectorID clusterID;
  6932. short amount;
  6933. TreasureCode treasureCode;
  6934. bool createNow;
  6935. };
  6936. DEF_STMT(TreasureInstance, "SELECT "
  6937. "Lifespan, "
  6938. "TreasureID, "
  6939. "LocationX, LocationY, LocationZ, "
  6940. "SectorID, "
  6941. "Amount, TreasureCode "
  6942. "FROM TreasureInstances "
  6943. "WHERE MapID = ?"
  6944. )
  6945. SQL_FLT4_PARM(TreasureInstance_Lifespan, SQL_OUT_PARM)
  6946. SQL_INT2_PARM(TreasureInstance_TreasureID, SQL_OUT_PARM)
  6947. SQL_FLT4_PARM(TreasureInstance_xLocation, SQL_OUT_PARM)
  6948. SQL_FLT4_PARM(TreasureInstance_yLocation, SQL_OUT_PARM)
  6949. SQL_FLT4_PARM(TreasureInstance_zLocation, SQL_OUT_PARM)
  6950. SQL_INT2_PARM(TreasureInstance_SectorID, SQL_OUT_PARM)
  6951. SQL_INT2_PARM(TreasureInstance_Amount, SQL_OUT_PARM)
  6952. SQL_INT2_PARM(TreasureInstance_TreasureCode, SQL_OUT_PARM)
  6953. SQL_INT2_PARM(TreasureInstance_MapID, SQL_IN_PARM)
  6954. */
  6955. SQL_GO(TreasureInstance);
  6956. while(SQL_SUCCEEDED(SQL_GETROW(TreasureInstance)))
  6957. {
  6958. DataTreasureIGC obj;
  6959. obj.lifespan = TreasureInstance_Lifespan;
  6960. obj.objectID = -(pMission->GenerateNewTreasureID() + 2); //NYI hack: need explicit, unique object ID
  6961. obj.treasureID = TreasureInstance_TreasureID;
  6962. obj.p0 = Vector (TreasureInstance_xLocation, TreasureInstance_yLocation, TreasureInstance_zLocation);
  6963. obj.v0 = Vector (0.0f, 0.0f, 0.0f);
  6964. obj.time0 = 0;
  6965. obj.clusterID = TreasureInstance_SectorID;
  6966. obj.amount = TreasureInstance_Amount;
  6967. obj.treasureCode = TreasureInstance_TreasureCode;
  6968. obj.createNow = true;
  6969. printf (" Creating treasure %d...\n", TreasureInstance_TreasureID);
  6970. ItreasureIGC* punk = static_cast<ItreasureIGC*> (pMission->CreateObject (g.timeNow, OT_treasure, &obj, sizeof(obj)));
  6971. punk->SetCreateNow ();
  6972. punk->Release();
  6973. }
  6974. //-----------------------------------------------------------------
  6975. printf (" Writing %s.igc ...", pMaps[i].szFileName);
  6976. if (DumpIGCFile (pMaps[i].szFileName, pMission, c_maskMapTypes))
  6977. printf (" OK\n\n");
  6978. else
  6979. {
  6980. printf (" FAILED\n\n");
  6981. exit (1);
  6982. }
  6983. pMission->Terminate ();
  6984. delete pMission;
  6985. }
  6986. }
  6987. #pragma warning(pop)
  6988. #endif // !defined(ALLSRV_STANDALONE)
  6989. }
  6990. /*-------------------------------------------------------------------------
  6991. * RegisterAllSrv()
  6992. *-------------------------------------------------------------------------
  6993. * Purpose:
  6994. * Register AllSrv as an EXE or NT Service, and report any errors
  6995. *
  6996. * Paramters:
  6997. * fReRegister: are we registering after an autoupdate?
  6998. * fInstallAsService: TRUE if NT service, FALSE for EXE
  6999. */
  7000. void RegisterAllSrv(BOOL fReRegister, BOOL fInstallAsService, int argc, char *argv[])
  7001. {
  7002. if (fInstallAsService && IsWin9x())
  7003. {
  7004. printf("Cannot register as a service on this operating system.\n");
  7005. return;
  7006. }
  7007. DWORD dwErrorCode(_Module.RegisterServer(fReRegister, TRUE, fInstallAsService, argc, argv));
  7008. if (FAILED(dwErrorCode))
  7009. {
  7010. char szBuf[MAX_PATH];
  7011. sprintf(szBuf, "Unable to register server [0x%08x]. %s is not properly installed.\n", dwErrorCode, argv[0]);
  7012. PrintSystemErrorMessage(szBuf, dwErrorCode);
  7013. }
  7014. else
  7015. {
  7016. // signal success in terms of adding COM related registry stuff
  7017. printf("Registry Update Successful.\n");
  7018. }
  7019. }
  7020. /*-------------------------------------------------------------------------
  7021. * UnregisterSvr()
  7022. *-------------------------------------------------------------------------
  7023. * Purpose:
  7024. * Unregister AllSrv as an EXE or NT Service, and report any errors
  7025. *
  7026. */
  7027. void UnregisterAllSrv(char *szServerName)
  7028. {
  7029. DWORD dwErrorCode(_Module.UnregisterServer());
  7030. if (FAILED(dwErrorCode))
  7031. {
  7032. char szBuf[MAX_PATH];
  7033. sprintf(szBuf, "Unable to remove server [0x%08x]. %s is not properly uninstalled.\n", dwErrorCode, szServerName);
  7034. PrintSystemErrorMessage(szBuf, dwErrorCode);
  7035. }
  7036. else
  7037. {
  7038. // signal success in terms of removing COM related registry stuff
  7039. printf("Registry Update Successful.\n");
  7040. }
  7041. }
  7042. ////////////////////////////////////////////////////////////////////////
  7043. // main
  7044. //
  7045. // This is the entry point to the process, whether run as a service, or
  7046. // as an executable. Since the NT service manager does not send us
  7047. // command line flags, the default case is to run as a service.
  7048. // Additional supported mutually-exclusive functions are installation
  7049. // (with the -install command line switch) de-installation (with the
  7050. // -remove switch) and running as a non-service executable (with the
  7051. // -debug switch).
  7052. //
  7053. // This function parses the command line arguments and invokes the
  7054. // selected mode of operation. When running as an executable, blocking
  7055. // keyboard input is used to prevent the program from exiting prematurely.
  7056. //
  7057. int __cdecl main(int argc, char *argv[])
  7058. {
  7059. // Load the global settings from the registry
  7060. LoadSettings();
  7061. // set the FPU to double precision, instead of long double
  7062. // (this makes FP compares a little safer)
  7063. if (g.fDoublePrecision)
  7064. _controlfp(_PC_53, _MCW_PC);
  7065. // seed the random number generator with the current time
  7066. // (GetTickCount may be semi-predictable on server startup, so we add the
  7067. // clock time to shake things up a bit)
  7068. srand(GetTickCount() + time(NULL));
  7069. // Enter this thread into the COM MTA
  7070. TCCoInit init(COINIT_MULTITHREADED);
  7071. ZSucceeded((HRESULT)init);
  7072. if (init.Failed())
  7073. return (HRESULT)init;
  7074. // Set the process-wide COM security
  7075. // This provides a NULL DACL which will allow access to everyone.
  7076. PSECURITY_DESCRIPTOR psd = NULL;
  7077. CSecurityDescriptor sd;
  7078. if (IsWinNT())
  7079. {
  7080. sd.InitializeFromThreadToken();
  7081. psd = sd;
  7082. }
  7083. HRESULT hr;
  7084. hr = CoInitializeSecurity(psd, -1, NULL, NULL,
  7085. RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
  7086. ZSucceeded(hr);
  7087. RETURN_FAILED(hr);
  7088. // Initialize AGC
  7089. ZSucceeded(hr = _Module.InitAGC());
  7090. RETURN_FAILED(hr);
  7091. char szSvcName[64];
  7092. strcpy(szSvcName, c_szSvcName);
  7093. SERVICE_TABLE_ENTRY rgSTE[] =
  7094. {
  7095. { szSvcName, (LPSERVICE_MAIN_FUNCTION)_ServiceMain },
  7096. { NULL, NULL }
  7097. };
  7098. bool fShowUsage = false;
  7099. g.hInst = GetModuleHandle(NULL);
  7100. if (argc < 1)
  7101. {
  7102. //printf ("Hell has frozen over.\n"); // this should never happen
  7103. // Terminate AGC
  7104. _Module.TermAGC();
  7105. return 0;
  7106. }
  7107. // if registering (after an AutoUpdate)--be sure to keep existing install data intact
  7108. if (argc > 1 && (*argv[1]=='-' || *argv[1]=='/') && !lstrcmpi ("reregister", argv[1]+1))
  7109. {
  7110. RegisterAllSrv(TRUE, _Module.IsInstalledAsService(), argc, argv);
  7111. // Terminate AGC
  7112. _Module.TermAGC();
  7113. return 0;
  7114. }
  7115. // Check for a weird/busted installation...this really can happen
  7116. if (_Module.IsInstalledAsService() && !_Module.IsInServiceControlManager())
  7117. {
  7118. printf("Incomplete installation detected. Removing incomplete installation...\n");
  7119. UnregisterAllSrv(argv[0]);
  7120. }
  7121. _Module.SetCOMStarted(argc == 2 && !lstrcmpi("-Embedding",argv[1]));
  7122. #if defined(ALLSRV_STANDALONE)
  7123. #ifndef _DEBUG
  7124. if (argc == 1 && !_Module.IsInstalledAsService() && !_Module.WasCOMStarted())
  7125. {
  7126. printf("\nUse AllSrvUI32.exe to run the Allegiance Server.\n");
  7127. // Terminate AGC
  7128. _Module.TermAGC();
  7129. return 0;
  7130. }
  7131. #endif
  7132. #endif
  7133. if (argc > 1 && (*argv[1]=='-' || *argv[1]=='/') && !_Module.WasCOMStarted())
  7134. {
  7135. BOOL fInstallAsService = !lstrcmpi("Service",argv[1]+1);
  7136. if (fInstallAsService || !lstrcmpi("RegServer",argv[1]+1))
  7137. {
  7138. RegisterAllSrv(FALSE, fInstallAsService, argc, argv);
  7139. }
  7140. else if (!lstrcmpi("UnregServer",argv[1]+1) || !lstrcmpi("Remove",argv[1]+1))
  7141. {
  7142. UnregisterAllSrv(argv[0]);
  7143. }
  7144. #if !defined(ALLSRV_STANDALONE)
  7145. else if (!lstrcmpi ("BuildIGCFiles", argv[1]+1))
  7146. {
  7147. if (argc > 2)
  7148. BuildIGCFiles (argv[2], false, true);
  7149. else
  7150. printf ("Please provide a path for IGC files to get written to...\n");
  7151. }
  7152. else if (!lstrcmpi ("EncryptIGCFiles", argv[1]+1))
  7153. {
  7154. if (argc > 2)
  7155. BuildIGCFiles (argv[2], true, true);
  7156. else
  7157. printf ("Please provide a path for IGC files to get written to...\n");
  7158. }
  7159. else if (!lstrcmpi ("BuildIGCFilesNM", argv[1]+1))
  7160. {
  7161. if (argc > 2)
  7162. BuildIGCFiles (argv[2], false, false);
  7163. else
  7164. printf ("Please provide a path for IGC files to get written to...\n");
  7165. }
  7166. else if (!lstrcmpi ("EncryptIGCFilesNM", argv[1]+1))
  7167. {
  7168. if (argc > 2)
  7169. BuildIGCFiles (argv[2], true, false);
  7170. else
  7171. printf ("Please provide a path for IGC files to get written to...\n");
  7172. }
  7173. #endif // !defined(ALLSRV_STANDALONE)
  7174. else
  7175. {
  7176. // if unknown args
  7177. fShowUsage = true;
  7178. }
  7179. }
  7180. else
  7181. {
  7182. if (argc == 1 || _Module.WasCOMStarted())
  7183. {
  7184. if (_Module.IsInstalledAsService())
  7185. {
  7186. printf("Trying to run %s as an NT Service...\nType \"%s -RegServer\" to re-install as EXE.\n", argv[0], argv[0]);
  7187. //start as service
  7188. if (!StartServiceCtrlDispatcher(rgSTE))
  7189. _AGCModule.TriggerEvent(NULL, AllsrvEventID_StartingService, "", -1, -1, -1, 0);
  7190. }
  7191. else
  7192. {
  7193. if (!_Module.IsInstalled())
  7194. {
  7195. printf("\nInstalling %s as an EXE...\n", argv[0]);
  7196. RegisterAllSrv(FALSE, FALSE, argc, argv); // FALSE == register as EXE
  7197. }
  7198. _Module.RunAsExecutable();
  7199. }
  7200. }
  7201. else // if unknown args
  7202. fShowUsage = true;
  7203. }
  7204. if (fShowUsage)
  7205. {
  7206. ZVersionInfo vi;
  7207. printf("%s\n%s\n\n",
  7208. (LPCSTR)vi.GetFileDescription(), (LPCSTR)vi.GetLegalCopyright());
  7209. if (!_Module.IsInstalled())
  7210. {
  7211. printf("\nThe server needs to be installed before running.\n\n", argv[0]);
  7212. }
  7213. // ----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8
  7214. printf("usage is ONE of:\n\n");
  7215. printf("AllSrv starts the server if registered as an exe\n");
  7216. printf("AllSrv -RegServer install the server as an EXE\n");
  7217. if (IsWinNT())
  7218. printf("AllSrv -Service [user pw] install the server as an NT Service\n");
  7219. printf("AllSrv -UnregServer uninstall the server\n");
  7220. #if !defined(ALLSRV_STANDALONE)
  7221. printf("AllSrv -BuildIGCFiles <artpath> builds igc files from database\n");
  7222. printf(" (see root makefile for required env vars).\n");
  7223. printf("AllSrv -EncryptIGCFiles <artpath> builds & encrypts igc files from database\n");
  7224. printf(" (see root makefile for required env vars).\n");
  7225. printf("AllSrv -BuildIGCFilesNM <artpath> as above, but no maps\n");
  7226. printf(" (see root makefile for required env vars).\n");
  7227. printf("AllSrv -EncryptIGCFilesNM <artpath> as above, but no maps\n");
  7228. printf(" (see root makefile for required env vars).\n");
  7229. #endif // !defined(ALLSRV_STANDALONE)
  7230. if (IsWinNT())
  7231. printf("\nTo start this service (when installed as NT service): net start %s\n",c_szSvcName);
  7232. // ----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8
  7233. }
  7234. // Terminate AGC
  7235. _Module.TermAGC();
  7236. // Indicate success
  7237. return 0;
  7238. }
  7239. HRESULT pascal FedSrv_Pause()
  7240. {
  7241. if (!g.fPaused)
  7242. {
  7243. g.fPaused = true;
  7244. PostThreadMessage(g.idReceiveThread, wm_fs_pause, 0, 0);
  7245. }
  7246. return S_OK;
  7247. }
  7248. HRESULT pascal FedSrv_Continue()
  7249. {
  7250. if (g.fPaused)
  7251. {
  7252. g.fPaused = false;
  7253. PostThreadMessage(g.idReceiveThread, wm_fs_pause, 0, 0);
  7254. }
  7255. return S_OK;
  7256. }
  7257. /*-------------------------------------------------------------------------
  7258. * FedSrv_Init
  7259. *-------------------------------------------------------------------------
  7260. * Purpose:
  7261. * Saves instance handle and creates main window. Does lots of setup stuff
  7262. *
  7263. * Side Effects:
  7264. * Lots
  7265. */
  7266. HRESULT pascal FedSrv_Init()
  7267. {
  7268. int iCon = 0;
  7269. HRESULT hr = S_OK;
  7270. _AGCModule.TriggerEvent(NULL, AllsrvEventID_Initialize, "", -1, -1, -1, 0);
  7271. // Set cur dir = exe dir
  7272. HMODULE hmod = GetModuleHandle(NULL);
  7273. assert(hmod);
  7274. char path_buffer[_MAX_PATH];
  7275. GetModuleFileName(hmod, path_buffer, sizeof(path_buffer));
  7276. char drive[_MAX_DRIVE];
  7277. char dir[_MAX_DIR];
  7278. // just strip off the filename.extension
  7279. _splitpath(path_buffer, drive, dir, NULL, NULL);
  7280. _makepath(path_buffer, drive, dir, NULL, NULL);
  7281. SetCurrentDirectory(path_buffer);
  7282. LoadTechBits(); // only for the IfYouBuildIt cheat
  7283. bool fSWMRGInit = FSWMRGInitialize(&g.swmrg, "FedSrv");
  7284. ZAssert(fSWMRGInit);
  7285. // Initialize COM library
  7286. // ZSucceeded(CoInitialize(NULL));
  7287. ZVerify(g.perfshare.Initialize());
  7288. //
  7289. // Allocate perfmon counters for the server
  7290. //
  7291. g.pServerCounters = (SERVER_COUNTERS *)g.perfshare.AllocateCounters(
  7292. "AllSrv",
  7293. "0", // if there are ever multiple servers running, change this
  7294. sizeof(SERVER_COUNTERS));
  7295. memset(g.pServerCounters, 0x00, sizeof(SERVER_COUNTERS));
  7296. #if !defined(ALLSRV_STANDALONE)
  7297. g.pzas = CreateZoneAuthServer();
  7298. if (!g.pzas)
  7299. {
  7300. _AGCModule.TriggerEvent(NULL, AllsrvEventID_ZoneAuthServer, "", -1, -1, -1, 0);
  7301. return E_FAIL;
  7302. }
  7303. #endif // !defined(ALLSRV_STANDALONE)
  7304. // create event used by fm to signal a message has arrived
  7305. // let's try NOT signaling on this event, since it may signal us too often
  7306. // g.hPlayerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  7307. // assert(g.hPlayerEvent);
  7308. // create event used to signal that the receive thread should exit
  7309. g.hKillReceiveEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  7310. assert(g.hKillReceiveEvent);
  7311. g.idReceiveThread = 0; // id of receive thread
  7312. // create thread to receive player messages and start things cranking
  7313. g.hReceiveThread = CreateThread(NULL, 0, ReceiveThread, 0, 0, &g.idReceiveThread);
  7314. assert(g.hReceiveThread);
  7315. ZVerify (TIMERR_NOERROR == timeBeginPeriod(1));
  7316. return hr;
  7317. }
  7318. // ********** Game Site Implementation ************
  7319. class ThingSiteImpl : public ThingSite
  7320. {
  7321. public:
  7322. ThingSiteImpl(ImodelIGC* pmodel)
  7323. :
  7324. m_pmodel(pmodel) //Do not AddRef() ... lifespan < lifespan of model
  7325. {
  7326. ModelAttributes ma = pmodel->GetAttributes();
  7327. m_bSideVisibility = (ma & c_mtSeenBySide) != 0;
  7328. m_bPredictable = (ma & c_mtPredictable) != 0;
  7329. }
  7330. virtual ~ThingSiteImpl(void)
  7331. {
  7332. }
  7333. virtual void Terminate(void)
  7334. {
  7335. }
  7336. void SetCluster(ImodelIGC* pmodel, IclusterIGC* pcluster)
  7337. {
  7338. assert (pmodel);
  7339. if (m_bSideVisibility)
  7340. {
  7341. if (!m_bPredictable)
  7342. {
  7343. IsideIGC* psideModel = pmodel->GetSide();
  7344. assert (psideModel);
  7345. for (SideLinkIGC* l = pmodel->GetMission()->GetSides()->first();
  7346. (l != NULL);
  7347. l = l->next())
  7348. {
  7349. IsideIGC* pside = l->data();
  7350. //You always lose contact when a non-static object switches sectors
  7351. if (pside != psideModel)
  7352. {
  7353. m_rgSideVisibility[pside->GetObjectID()].fVisible(false);
  7354. HideObject(pside, pmodel);
  7355. }
  7356. }
  7357. }
  7358. if (pcluster)
  7359. UpdateSideVisibility(pmodel, pcluster);
  7360. }
  7361. }
  7362. static bool ShowObject(IsideIGC* pside, ImodelIGC* pmodel)
  7363. {
  7364. assert (pside);
  7365. assert (pmodel);
  7366. bool bSent = false;
  7367. ObjectType type = pmodel->GetObjectType();
  7368. if (pmodel->GetAttributes() & c_mtPredictable)
  7369. {
  7370. CFSMission* pfsMission = (CFSMission *)(pside->GetMission()->GetPrivateData());
  7371. assert (pfsMission);
  7372. //Ships are never predictable
  7373. assert (type != OT_ship);
  7374. if (pside->GetActiveF() &&
  7375. ((pfsMission->GetStage() == STAGE_STARTED) ||
  7376. (pfsMission->GetStage() == STAGE_STARTING)))
  7377. {
  7378. SideID sideID = pside->GetObjectID();
  7379. // this is called when a stream of messages to a different recipient is in process of being queued-up
  7380. // so we will use the secondary (smaller 1k) outbox so as to not interrupt the main message stream
  7381. g.fm.UseMainOutBox(false);
  7382. g.fm.SetDefaultRecipient(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED);
  7383. //Tell everyone on the side about the newly visible static object
  7384. bSent = true;
  7385. ExportObj(pmodel, type, NULL);
  7386. //perform a hack here: if a aleph becomes visible, make its corresponding aleph visible
  7387. if (type == OT_warp)
  7388. {
  7389. IwarpIGC* pwarpOther = ((IwarpIGC*)pmodel)->GetDestination();
  7390. assert (pwarpOther);
  7391. ExportObj(pwarpOther, OT_warp, NULL);
  7392. assert (!pwarpOther->SeenBySide(pside));
  7393. {
  7394. //Force the visibility of this warp wrt the side to true without going through
  7395. //SetSideVisibility ... do it via a ugly forced upcast
  7396. ThingSiteImpl* pts = (ThingSiteImpl*)(pwarpOther->GetThingSite());
  7397. SideVisibility& sv = pts->m_rgSideVisibility[sideID];
  7398. sv.fVisible(true);
  7399. }
  7400. {
  7401. for (WarpBombLink* p = ((IwarpIGC*)pmodel)->GetBombs()->first(); (p != NULL); p = p->next())
  7402. {
  7403. BEGIN_PFM_CREATE(g.fm, pfmWB, S, WARP_BOMB)
  7404. END_PFM_CREATE
  7405. pfmWB->timeExplosion = p->data().timeExplosion;
  7406. pfmWB->warpidBombed = pmodel->GetObjectID();
  7407. pfmWB->expendableidMissile = p->data().pmt->GetObjectID();
  7408. }
  7409. }
  7410. {
  7411. for (WarpBombLink* p = pwarpOther->GetBombs()->first(); (p != NULL); p = p->next())
  7412. {
  7413. BEGIN_PFM_CREATE(g.fm, pfmWB, S, WARP_BOMB)
  7414. END_PFM_CREATE
  7415. pfmWB->timeExplosion = p->data().timeExplosion;
  7416. pfmWB->warpidBombed = pwarpOther->GetObjectID();
  7417. pfmWB->expendableidMissile = p->data().pmt->GetObjectID();
  7418. }
  7419. }
  7420. }
  7421. else if (type == OT_asteroid)
  7422. {
  7423. //if under construction, send the building effect
  7424. IbuildingEffectIGC* pbe = ((IasteroidIGC*)pmodel)->GetBuildingEffect();
  7425. if (pbe)
  7426. ExportObj(pbe, OT_buildingEffect, NULL);
  7427. }
  7428. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  7429. }
  7430. }
  7431. else if (type == OT_ship)
  7432. {
  7433. CFSShip* pfsShip = (CFSShip *)(((IshipIGC*)pmodel)->GetPrivateData());
  7434. assert (pfsShip);
  7435. pfsShip->ShipStatusSpotted(pside);
  7436. }
  7437. g.fm.UseMainOutBox(true);
  7438. return bSent;
  7439. }
  7440. static void HideObject(IsideIGC* pside, ImodelIGC* pmodel)
  7441. {
  7442. // let the team know that they can no longer see this ship
  7443. if (pmodel->GetObjectType() == OT_ship)
  7444. {
  7445. CFSShip* pfsShip = (CFSShip *)(((IshipIGC*)pmodel)->GetPrivateData());
  7446. assert (pfsShip);
  7447. pfsShip->ShipStatusHidden(pside);
  7448. }
  7449. }
  7450. virtual void UpdateSideVisibility(ImodelIGC* pmodel,
  7451. IclusterIGC* pcluster)
  7452. {
  7453. assert (pmodel == m_pmodel);
  7454. //We can only update it if we have one
  7455. if (m_bSideVisibility)
  7456. {
  7457. ClusterSite* pcs = pcluster->GetClusterSite();
  7458. for (SideLinkIGC* l = pcluster->GetMission()->GetSides()->first();
  7459. (l != NULL);
  7460. l = l->next())
  7461. {
  7462. IsideIGC* pside = l->data();
  7463. SideID sid = pside->GetObjectID();
  7464. assert (sid >= 0);
  7465. assert (sid < c_cSidesMax);
  7466. SideVisibility& sv = m_rgSideVisibility[sid];
  7467. //Update the visibility for everything except visible static objects
  7468. if (!(m_bPredictable && sv.fVisible()))
  7469. {
  7470. if (pside == pmodel->GetSide())
  7471. {
  7472. //Always see object on your own side (& call ShowObject() to export new stations, etc.)
  7473. sv.fVisible(true);
  7474. ShowObject(pside, pmodel);
  7475. }
  7476. else if (sv.pLastSpotter() && sv.pLastSpotter()->InScannerRange(pmodel))
  7477. {
  7478. if (!sv.fVisible())
  7479. {
  7480. //See it
  7481. sv.fVisible(true);
  7482. ShowObject(pside, pmodel);
  7483. }
  7484. }
  7485. else
  7486. {
  7487. //Not trivially seen ... loop over everything that could see it
  7488. sv.fVisible(false);
  7489. for (ScannerLinkIGC* l = pcs->GetScanners(sid)->first();
  7490. (l != NULL);
  7491. l = l->next())
  7492. {
  7493. IscannerIGC* s = l->data();
  7494. assert (s->GetCluster() == pcluster);
  7495. if (s->InScannerRange(pmodel))
  7496. {
  7497. //See it
  7498. sv.fVisible(true);
  7499. bool bSent = ShowObject(pside, pmodel);
  7500. //Keep track of who spotted it
  7501. sv.pLastSpotter(s);
  7502. if (bSent)
  7503. {
  7504. // if it's a station, aleph, special asteroid or helium asteroid...
  7505. ObjectType otModel = pmodel->GetObjectType();
  7506. if (otModel == OT_station || otModel == OT_warp ||
  7507. (otModel == OT_asteroid &&
  7508. (static_cast<IasteroidIGC*>(pmodel)->GetCapabilities() & ~c_aabmBuildable) != 0))
  7509. {
  7510. ObjectType otSpotter = s->GetObjectType();
  7511. // tell the side who spotted it
  7512. BEGIN_PFM_CREATE(g.fm, pfmObjectSpotted, S, OBJECT_SPOTTED)
  7513. END_PFM_CREATE
  7514. pfmObjectSpotted->otObject = otModel;
  7515. pfmObjectSpotted->oidObject = pmodel->GetObjectID();
  7516. pfmObjectSpotted->otSpotter = otSpotter;
  7517. pfmObjectSpotted->oidSpotter = s->GetObjectID();
  7518. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  7519. if ((otModel == OT_warp) || (otModel == OT_asteroid))
  7520. {
  7521. if (otSpotter == OT_ship)
  7522. {
  7523. IshipIGC* pship = (IshipIGC*)s;
  7524. CFSShip* pfsship = (CFSShip*)(pship->GetPrivateData());
  7525. PlayerScoreObject* ppso = pfsship->GetPlayerScoreObject();
  7526. if (otModel == OT_warp)
  7527. ppso->SpotWarp();
  7528. else
  7529. {
  7530. assert (otModel == OT_asteroid);
  7531. ppso->SpotSpecialAsteroid();
  7532. }
  7533. }
  7534. }
  7535. }
  7536. }
  7537. break;
  7538. }
  7539. }
  7540. if (!sv.fVisible())
  7541. {
  7542. // hide it
  7543. HideObject(pside, pmodel);
  7544. sv.pLastSpotter(NULL);
  7545. }
  7546. }
  7547. }
  7548. }
  7549. }
  7550. }
  7551. virtual bool GetSideVisibility(IsideIGC* pside)
  7552. {
  7553. assert (pside != NULL);
  7554. return m_bSideVisibility ? m_rgSideVisibility[pside->GetObjectID()].fVisible() : true;
  7555. }
  7556. virtual void SetSideVisibility(IsideIGC* pside,
  7557. bool fVisible)
  7558. {
  7559. assert (pside);
  7560. if (m_bSideVisibility)
  7561. {
  7562. SideID sid = pside->GetObjectID();
  7563. SideVisibility& sv = m_rgSideVisibility[sid];
  7564. if (fVisible != sv.fVisible())
  7565. {
  7566. sv.fVisible(fVisible);
  7567. if (fVisible)
  7568. ShowObject(pside, m_pmodel);
  7569. else
  7570. HideObject(pside, m_pmodel);
  7571. }
  7572. }
  7573. }
  7574. private:
  7575. SideVisibility m_rgSideVisibility[c_cSidesMax];
  7576. ImodelIGC* m_pmodel;
  7577. bool m_bPredictable;
  7578. bool m_bSideVisibility;
  7579. };
  7580. class ClusterSiteImpl : public ClusterSite
  7581. {
  7582. public:
  7583. ClusterSiteImpl(IclusterIGC * pcluster)
  7584. :
  7585. m_pcluster(pcluster)
  7586. {
  7587. CFSMission * pfsMission = (CFSMission *) pcluster->GetMission()->GetPrivateData();
  7588. }
  7589. virtual void AddScanner(SideID sid, IscannerIGC* scannerNew)
  7590. {
  7591. assert (sid >= 0);
  7592. assert (sid < c_cSidesMax);
  7593. assert (scannerNew);
  7594. AddIbaseIGC((BaseListIGC*)&(m_scanners[sid]), scannerNew);
  7595. }
  7596. virtual void DeleteScanner(SideID sid, IscannerIGC* scannerOld)
  7597. {
  7598. assert (sid >= 0);
  7599. assert (sid < c_cSidesMax);
  7600. assert (scannerOld);
  7601. DeleteIbaseIGC((BaseListIGC*)&(m_scanners[sid]), scannerOld);
  7602. }
  7603. virtual const ScannerListIGC* GetScanners(SideID sid) const
  7604. {
  7605. assert (sid >= 0);
  7606. assert (sid < c_cSidesMax);
  7607. return &(m_scanners[sid]);
  7608. }
  7609. virtual void Terminate()
  7610. {
  7611. ((CFSMission *)m_pcluster->GetMission()->GetPrivateData())->DeleteCluster(m_pcluster); // destroy FS cluster
  7612. ClusterSite::Terminate();
  7613. }
  7614. private:
  7615. IclusterIGC* m_pcluster;
  7616. ScannerListIGC m_scanners[c_cSidesMax];
  7617. };
  7618. TRef<ThingSite> FedSrvSiteBase::CreateThingSite(ImodelIGC* pModel)
  7619. {
  7620. return new ThingSiteImpl(pModel);
  7621. }
  7622. TRef<ClusterSite> FedSrvSiteBase::CreateClusterSite(IclusterIGC* pCluster)
  7623. {
  7624. m_pfsMission->CreateCluster(pCluster); // make a FS cluster
  7625. return new ClusterSiteImpl(pCluster);
  7626. }
  7627. void FedSrvSiteBase::UpgradeDrones(IsideIGC* pside)
  7628. {
  7629. {
  7630. //Upgrade any drones docked at the station
  7631. for (StationLinkIGC* pslStation = pside->GetStations()->first();
  7632. (pslStation != NULL);
  7633. pslStation = pslStation->next())
  7634. {
  7635. IstationIGC* pstation = pslStation->data();
  7636. for (ShipLinkIGC* pslShip = pstation->GetShips()->first();
  7637. (pslShip != NULL);
  7638. pslShip = pslShip->next())
  7639. {
  7640. IshipIGC* pship = pslShip->data();
  7641. CFSShip* pfsship = (CFSShip*)(pship->GetPrivateData());
  7642. if (!pfsship->IsPlayer())
  7643. UpgradeShip(pship, pstation);
  7644. }
  7645. }
  7646. }
  7647. }
  7648. void FedSrvSiteBase::DevelopmentCompleted(IbucketIGC * pbucket, IdevelopmentIGC * pdev, Time now)
  7649. {
  7650. IsideIGC * pside = pbucket->GetSide();
  7651. SideID sideid = pside->GetObjectID();
  7652. pside->ApplyDevelopmentTechs(pdev->GetEffectTechs());
  7653. BEGIN_PFM_CREATE(g.fm, pfmDevCompleted, S, DEV_COMPLETED)
  7654. END_PFM_CREATE
  7655. pfmDevCompleted->devId = pdev->GetObjectID();
  7656. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  7657. if (!pdev->GetTechOnly())
  7658. pside->ApplyGlobalAttributeSet(pdev->GetGlobalAttributeSet());
  7659. if (pdev->GetObjectID() == c_didTeamMoney)
  7660. {
  7661. static char szReason[100];
  7662. sprintf(szReason, "%s has proven the economic value of their outpost.", pside->GetName());
  7663. m_pfsMission->GameOver(pside, szReason);
  7664. }
  7665. }
  7666. void FedSrvSiteBase::SideDevelopmentTechChange(IsideIGC* pside)
  7667. {
  7668. assert (pside);
  7669. if (m_pfsMission->GetStage() == STAGE_STARTED)
  7670. {
  7671. SideID sideid = pside->GetObjectID();
  7672. const TechTreeBitMask& ttbmDev = pside->GetDevelopmentTechs();
  7673. BEGIN_PFM_CREATE(g.fm, pfmSideTechChange, S, SIDE_TECH_CHANGE)
  7674. END_PFM_CREATE
  7675. pfmSideTechChange->sideID = sideid;
  7676. pfmSideTechChange->ttbmDevelopments = ttbmDev;
  7677. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  7678. }
  7679. for (StationLinkIGC* pslStation = pside->GetStations()->first();
  7680. (pslStation != NULL);
  7681. pslStation = pslStation->next())
  7682. {
  7683. IstationIGC* pstation = pslStation->data();
  7684. IstationTypeIGC* pstOld = pstation->GetBaseStationType();
  7685. IstationTypeIGC* pstSuccessor = pstOld->GetSuccessorStationType(pside);
  7686. if (pstOld != pstSuccessor)
  7687. {
  7688. pstation->SetName(pstSuccessor->GetName());
  7689. pstation->SetBaseStationType(pstSuccessor);
  7690. }
  7691. }
  7692. UpgradeDrones(pside);
  7693. }
  7694. void FedSrvSiteBase::SideGlobalAttributeChange(IsideIGC* pside)
  7695. {
  7696. assert (pside);
  7697. BEGIN_PFM_CREATE(g.fm, pfmSideAttrChange, S, SIDE_ATTRIBUTE_CHANGE)
  7698. END_PFM_CREATE
  7699. pfmSideAttrChange->sideID = pside->GetObjectID();
  7700. pfmSideAttrChange->gasAttributes = pside->GetGlobalAttributeSet();
  7701. g.fm.SendMessages(m_pfsMission->GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  7702. }
  7703. void FedSrvSiteBase::HullTypeCompleted(IsideIGC* pside, IhullTypeIGC* pht)
  7704. {
  7705. pside->AddToStockpile(pht);
  7706. //NYI send messages
  7707. }
  7708. void FedSrvSiteBase::PartTypeCompleted(IsideIGC* pside, IpartTypeIGC* ppt)
  7709. {
  7710. pside->AddToStockpile(ppt, IlauncherTypeIGC::IsLauncherType(ppt->GetEquipmentType())
  7711. ? ppt->GetAmount(NULL)
  7712. : 1);
  7713. //NYI send messages
  7714. }
  7715. void FedSrvSiteBase::StationTypeCompleted(IbucketIGC * pbucket, IstationIGC* pstation, IstationTypeIGC * pstationtype, Time now)
  7716. {
  7717. //NYI decide on the correct hack for the placement of space stations
  7718. IsideIGC * pside = pbucket->GetSide();
  7719. assert (pside);
  7720. if (pside->GetActiveF())
  7721. {
  7722. //Hack alert
  7723. CFSDrone * pfsDrone = CreateStockDrone(pstationtype->GetConstructionDroneType(), pstation->GetSide(), pstationtype);
  7724. assert (pfsDrone);
  7725. pfsDrone->GetIGCShip()->SetStation(pstation); // start them at a station just like everyone else
  7726. pfsDrone->GetIGCShip()->PickDefaultOrder(pstation->GetCluster(), pstation->GetPosition(), true);
  7727. pfsDrone->GetIGCShip()->SetStation(NULL); //but quickly undock them
  7728. // request a rock.
  7729. if (pfsDrone->GetIGCShip()->GetCommandID(c_cmdCurrent) != c_cidBuild)
  7730. {
  7731. SendChat(pfsDrone->GetIGCShip(), CHAT_TEAM, pfsDrone->GetIGCShip()->GetSide()->GetObjectID(),
  7732. pstationtype->GetConstructorNeedRockSound(), "New constructor requesting asteroid.");
  7733. }
  7734. }
  7735. }
  7736. void FedSrvSiteBase::BuildStation(IasteroidIGC* pasteroid,
  7737. IsideIGC* pside,
  7738. IstationTypeIGC* pstationtype,
  7739. Time now)
  7740. {
  7741. //Allow for an upgrade to the station being available
  7742. pstationtype = pstationtype->GetSuccessorStationType(pside);
  7743. ImissionIGC* pmission = m_pfsMission->GetIGCMission();
  7744. IclusterIGC* pcluster = pasteroid->GetCluster();
  7745. DataStationIGC ds;
  7746. strcpy(ds.name, pstationtype->GetName());
  7747. ds.clusterID = pcluster->GetObjectID();
  7748. ds.position = pasteroid->GetPosition();
  7749. ds.forward.x = ds.forward.y = 0.0f;
  7750. ds.forward.z = 1.0f;
  7751. ds.up.x = ds.up.z = 0.0f;
  7752. ds.up.y = 1.0f;
  7753. ds.rotation.axis(ds.forward);
  7754. ds.rotation.angle(0.0f);
  7755. ds.sideID = pside->GetObjectID();
  7756. ds.stationID = pmission->GenerateNewStationID();
  7757. ds.stationTypeID = pstationtype->GetObjectID();
  7758. ds.bpHull = pasteroid->GetFraction();
  7759. ds.bpShield = 0.0f;
  7760. KillAsteroidEvent(pasteroid, false);
  7761. IstationIGC * pstationNew = (IstationIGC *) (pmission->CreateObject(now, OT_station, &ds, sizeof(ds)));
  7762. assert (pstationNew->SeenBySide(pside)); //Sides always see their own station (which will force the export)
  7763. pstationNew->Release();
  7764. UpgradeDrones(pside);
  7765. //Possibly the built themselves to a victory
  7766. IsideIGC* psideWin = m_pfsMission->CheckForVictoryByStationBuild(pside);
  7767. if (psideWin)
  7768. {
  7769. static char szReason[100]; //Make this static so we only need to keep a pointer to it around
  7770. sprintf(szReason, "%s won by out-building their opponents", psideWin->GetName());
  7771. m_pfsMission->GameOver(psideWin, szReason);
  7772. }
  7773. }
  7774. void FedSrvSiteBase::DroneTypeCompleted(IbucketIGC * pbucket, IstationIGC* pstation, IdroneTypeIGC * pdronetype, Time now)
  7775. {
  7776. IsideIGC* pside = pstation->GetSide();
  7777. if (pside->GetActiveF())
  7778. {
  7779. assert (pdronetype->GetPilotType() != c_ptBuilder);
  7780. CFSDrone * pfsDrone = CreateStockDrone(pdronetype, pside, (pdronetype->GetPilotType() == c_ptLayer)
  7781. ? pdronetype->GetLaidExpendable()
  7782. : NULL);
  7783. if (pfsDrone)
  7784. {
  7785. pfsDrone->GetIGCShip()->SetStation(pstation); //start them at a station just like everyone else
  7786. pfsDrone->GetIGCShip()->PickDefaultOrder(pstation->GetCluster(), pstation->GetPosition(), true);
  7787. pfsDrone->GetIGCShip()->SetStation(NULL); //but undock them immediately
  7788. // Tell everyone on your side that you got one of these things
  7789. // Can't send to team until ISideIGC keeps track of ships on a side
  7790. if (pdronetype->GetPilotType() == c_ptLayer)
  7791. {
  7792. if (pfsDrone->GetIGCShip()->GetBaseData()->GetObjectType() == OT_mineType)
  7793. SendChat(pfsDrone->GetIGCShip(), CHAT_TEAM, pfsDrone->GetIGCShip()->GetSide()->GetObjectID(),
  7794. droneWhereToLayMinefieldSound, "New minefield requesting location.");
  7795. else
  7796. SendChat(pfsDrone->GetIGCShip(), CHAT_TEAM, pfsDrone->GetIGCShip()->GetSide()->GetObjectID(),
  7797. droneWhereToLayTowerSound, "New tower requesting location.");
  7798. }
  7799. else if ((pdronetype->GetPilotType() == c_ptMiner) && (pfsDrone->GetIGCShip()->GetCommandID(c_cmdCurrent) != c_cidMine))
  7800. {
  7801. SendChat(pfsDrone->GetIGCShip(), CHAT_TEAM, pfsDrone->GetIGCShip()->GetSide()->GetObjectID(),
  7802. droneWhereToSound, "New miner requesting He3 asteriod.");
  7803. }
  7804. }
  7805. }
  7806. }
  7807. void FedSrvSiteBase::UpgradeShip(IshipIGC* pship, const IstationIGC* pstation)
  7808. {
  7809. assert (pship->GetParentShip() == NULL);
  7810. assert (pship->GetStation() == pstation);
  7811. //See if anything on the ship can be upgraded
  7812. IhullTypeIGC* pht = pship->GetBaseHullType();
  7813. assert (pht);
  7814. IhullTypeIGC* phtSuccessor = (IhullTypeIGC*)(pstation->GetSuccessor(pht));
  7815. if (pht != phtSuccessor)
  7816. {
  7817. //Nuke all the parts
  7818. {
  7819. const PartListIGC* pparts = pship->GetParts();
  7820. PartLinkIGC* ppl;
  7821. while (ppl = pparts->first())
  7822. ppl->data()->Terminate();
  7823. }
  7824. pship->SetBaseHullType(phtSuccessor);
  7825. //Buy a default loadout
  7826. {
  7827. for (PartTypeLinkIGC* ptl = phtSuccessor->GetPreferredPartTypes()->first();
  7828. (ptl != NULL);
  7829. ptl = ptl->next())
  7830. {
  7831. IpartTypeIGC* ppt = ptl->data();
  7832. if (pstation->CanBuy(ppt))
  7833. {
  7834. ppt = (IpartTypeIGC*)(pstation->GetSuccessor(ppt));
  7835. //Mount the part anyplace it can be mounted. Ignore price (included in the cost of the drone)
  7836. EquipmentType et = ppt->GetEquipmentType();
  7837. Mount mountMax = (et == ET_Weapon)
  7838. ? phtSuccessor->GetMaxWeapons()
  7839. : 1;
  7840. for (Mount i = 0; (i < mountMax); i++)
  7841. {
  7842. if ((pship->GetMountedPart(et, i) == NULL) && phtSuccessor->CanMount(ppt, i))
  7843. pship->CreateAndAddPart(ppt, i, 0x7fff);
  7844. }
  7845. }
  7846. }
  7847. }
  7848. }
  7849. else
  7850. {
  7851. //Check each of the parts. Work backwards through the list
  7852. //since new parts will be added to the end
  7853. PartLinkIGC* pplPart = pship->GetParts()->last();
  7854. while (pplPart)
  7855. {
  7856. IpartIGC* ppart = pplPart->data();
  7857. pplPart = pplPart->txen();
  7858. if (ppart->GetMountID() < 0)
  7859. ppart->Terminate(); //Trash anything in cargo
  7860. else
  7861. {
  7862. IpartTypeIGC* ppt = ppart->GetPartType();
  7863. IpartTypeIGC* pptSuccessor = (IpartTypeIGC*)(pstation->GetSuccessor(ppt));
  7864. if (pptSuccessor != ppt)
  7865. {
  7866. Mount mount = ppart->GetMountID();
  7867. ppart->Terminate();
  7868. pship->CreateAndAddPart(pptSuccessor, mount, 0x7fff);
  7869. }
  7870. }
  7871. }
  7872. }
  7873. }
  7874. void FedSrvSiteBase::TerminateModelEvent(ImodelIGC* pmodel)
  7875. {
  7876. }
  7877. void FedSrvSiteBase::KillShipEvent(Time timeCollision, IshipIGC* pship, ImodelIGC* pcredit, float amount, const Vector& p1, const Vector& p2)
  7878. {
  7879. ImissionIGC* pmission = m_pfsMission->GetIGCMission();
  7880. const MissionParams* pmp = pmission->GetMissionParams();
  7881. IsideIGC* pside = pship->GetSide();
  7882. IclusterIGC* pcluster = pship->GetCluster();
  7883. IshipIGC* pshipParent = pship->GetParentShip();
  7884. DamageBucketLink* pdmglink = NULL;
  7885. if (pshipParent == NULL)
  7886. {
  7887. //Transfer credit to the deserving individual, if we are tracking that sort of thing
  7888. DamageTrack* pdt = pship->GetDamageTrack();
  7889. if (pdt)
  7890. {
  7891. pdmglink = pdt->GetDamageBuckets()->first();
  7892. if (pdmglink)
  7893. {
  7894. if (pdmglink->data()->model()->GetMission() == pmission)
  7895. pcredit = pdmglink->data()->model();
  7896. DamageBucketLink* pblAssist = pdmglink->next();
  7897. while (pblAssist)
  7898. {
  7899. ImodelIGC* pAssist = pblAssist->data()->model();
  7900. if ((pAssist->GetObjectType() == OT_ship) &&
  7901. (pAssist->GetSide() != pside) &&
  7902. (pAssist->GetMission() == pmission))
  7903. {
  7904. CFSShip* pfsAssist = (CFSShip*)( ((IshipIGC*)pAssist)->GetPrivateData() );
  7905. if (pfsAssist->IsPlayer())
  7906. {
  7907. pfsAssist->GetPlayerScoreObject()->AddAssist();
  7908. break;
  7909. }
  7910. }
  7911. pblAssist = pblAssist->next();
  7912. }
  7913. }
  7914. }
  7915. //If we die, then all of our children die.
  7916. const ShipListIGC* pshipsChildren = pship->GetChildShips();
  7917. ShipLinkIGC* psl;
  7918. while (psl = pshipsChildren->first()) //Intentional assignment
  7919. {
  7920. KillShipEvent(timeCollision, psl->data(), pcredit, amount, p1, p2);
  7921. }
  7922. //Dead ship ... create treasures for all of its parts
  7923. const PartListIGC* plist = pship->GetParts();
  7924. PartLinkIGC* plink;
  7925. while (plink = plist->first()) //Not ==
  7926. {
  7927. IpartIGC* p = plink->data();
  7928. if (randomInt(0, 4) == 0)
  7929. CreateTreasure(timeCollision, pship, p, p->GetPartType(), p1, 100.0f, 60.0f);
  7930. p->Terminate();
  7931. }
  7932. //No ammo or fuel either
  7933. //Treasures for ammo
  7934. {
  7935. short ammo = pship->GetAmmo();
  7936. if (ammo > 0)
  7937. {
  7938. IpartTypeIGC* pptAmmo = pmission->GetAmmoPack();
  7939. assert (pptAmmo);
  7940. if (randomInt(0, 4) == 0)
  7941. {
  7942. CreateTreasure(timeCollision, pship, ammo, pptAmmo, p1, 100.0f, 30.0f);
  7943. }
  7944. pship->SetAmmo(0);
  7945. }
  7946. }
  7947. //Ditto for fuel
  7948. {
  7949. short fuel = short(pship->GetFuel());
  7950. if (fuel > 0)
  7951. {
  7952. IpartTypeIGC* pptFuel = pmission->GetFuelPack();
  7953. assert (pptFuel);
  7954. if (randomInt(0, 4) == 0)
  7955. {
  7956. CreateTreasure(timeCollision, pship, fuel, pptFuel, p1, 100.0f, 30.0f);
  7957. }
  7958. }
  7959. pship->SetFuel(0.0f);
  7960. }
  7961. //Jetison flags
  7962. {
  7963. SideID sidFlag = pship->GetFlag();
  7964. if (sidFlag != NA)
  7965. {
  7966. pship->SetFlag(NA);
  7967. const Vector& p = pship->GetPosition();
  7968. float lm = pmission->GetFloatConstant(c_fcidLensMultiplier);
  7969. float r = 1.5f * pmission->GetFloatConstant(c_fcidRadiusUniverse);
  7970. if (p.x*p.x + p.y*p.y + p.z*p.z/(lm*lm) > r*r)
  7971. {
  7972. RespawnFlag(sidFlag);
  7973. }
  7974. else
  7975. {
  7976. DataTreasureIGC dt;
  7977. dt.treasureCode = c_tcFlag;
  7978. dt.treasureID = sidFlag;
  7979. dt.amount = 0;
  7980. dt.lifespan = 3600.0f * 24.0f * 10.0f; //10 days
  7981. dt.createNow = false;
  7982. CreateTreasure(timeCollision, pship, &dt, p1, 100.0f);
  7983. }
  7984. BEGIN_PFM_CREATE(g.fm, pfmGain, S, GAIN_FLAG)
  7985. END_PFM_CREATE
  7986. pfmGain->sideidFlag = NA;
  7987. pfmGain->shipidRecipient = pship->GetObjectID();
  7988. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  7989. }
  7990. }
  7991. }
  7992. CFSShip * pfsShip = (CFSShip *) pship->GetPrivateData();
  7993. ShipID shipID = pship->GetObjectID();
  7994. //Adjust side moneys for the forced ejection or eject pod kill
  7995. bool bKilled;
  7996. bool bLifepod;
  7997. if (pfsShip->IsPlayer())
  7998. {
  7999. pfsShip->GetPlayer()->SetTreasureObjectID(NA);
  8000. bLifepod = (pshipParent == NULL) &&
  8001. pship->GetBaseHullType()->HasCapability(c_habmLifepod);
  8002. bKilled = (!pmp->bEjectPods) || bLifepod || (amount < 0.0f);
  8003. }
  8004. else
  8005. {
  8006. bKilled = true;
  8007. bLifepod = false;
  8008. }
  8009. if (bKilled)
  8010. pship->SetExperience(0.0f);
  8011. IshipIGC* pshipCredit = NULL;
  8012. if (amount != 0.0f)
  8013. {
  8014. BEGIN_PFM_CREATE(g.fm, pfmKillShip, S, KILL_SHIP)
  8015. END_PFM_CREATE
  8016. pfmKillShip->shipID = shipID;
  8017. pfmKillShip->bKillCredit = false;
  8018. pfmKillShip->bDeathCredit = bKilled;
  8019. pfmKillShip->sideidKiller = NA;
  8020. if (pcredit)
  8021. {
  8022. ObjectType type = pcredit->GetObjectType();
  8023. IsideIGC* psideCredit = pcredit->GetSide();
  8024. if (psideCredit)
  8025. {
  8026. //Killed by something that belongs to a side ... award appropriate money
  8027. pfmKillShip->sideidKiller = psideCredit->GetObjectID();
  8028. if ((!bLifepod) && (psideCredit != pside))
  8029. {
  8030. //Didn't slay a helpless eject pod or a team mate... award kill credit
  8031. pfmKillShip->bKillCredit = true;
  8032. if (type == OT_ship)
  8033. {
  8034. CFSShip* pfsshipCredit = (CFSShip*)(((IshipIGC*)pcredit)->GetPrivateData());
  8035. pfsshipCredit->AddKill();
  8036. PlayerScoreObject::AdjustCombatRating(pmission, pfsshipCredit->GetPlayerScoreObject(), pfsShip->GetPlayerScoreObject());
  8037. if (pside != psideCredit)
  8038. ((IshipIGC*)pcredit)->AddExperience();
  8039. if (pdmglink)
  8040. {
  8041. //Award score credit for partial kills to anyone who did damage in the damage track
  8042. float totalDamage = 0.0f;
  8043. {
  8044. for (DamageBucketLink* l = pdmglink; (l != NULL); l = l->next())
  8045. {
  8046. if (l->data()->model()->GetSide() != pside)
  8047. totalDamage += l->data()->totalDamage();
  8048. }
  8049. }
  8050. if (totalDamage != 0.0f)
  8051. {
  8052. for (DamageBucketLink* l = pdmglink; (l != NULL); l = l->next())
  8053. {
  8054. ImodelIGC* pmodel = l->data()->model();
  8055. if ((pmodel->GetObjectType() == OT_ship) &&
  8056. (pmodel->GetSide() != pside) &&
  8057. (pmodel->GetMission() == pmission))
  8058. {
  8059. IshipIGC* pshipCredit = (IshipIGC*)pmodel;
  8060. CFSShip* pfsShip = (CFSShip*)(pshipCredit->GetPrivateData());
  8061. pfsShip->GetPlayerScoreObject()->KillShip(pship, l->data()->totalDamage() / totalDamage);
  8062. IshipIGC* pshipParent = pshipCredit->GetParentShip();
  8063. if (pshipParent)
  8064. {
  8065. IhullTypeIGC* phtParent = pshipParent->GetBaseHullType();
  8066. assert (phtParent);
  8067. float divisor = 2.0f + float(phtParent->GetMaxWeapons() - phtParent->GetMaxFixedWeapons());
  8068. CFSShip* pfsParent = (CFSShip*)(pshipParent->GetPrivateData());
  8069. pfsParent->GetPlayerScoreObject()->KillShip(pship,
  8070. l->data()->totalDamage() /
  8071. (totalDamage * divisor));
  8072. }
  8073. }
  8074. }
  8075. }
  8076. }
  8077. }
  8078. else
  8079. {
  8080. psideCredit->AddKill();
  8081. }
  8082. IsideIGC* psideWin = m_pfsMission->CheckForVictoryByKills(psideCredit);
  8083. if (psideWin)
  8084. {
  8085. static char szReason[100]; //Make this static so we only need to keep a pointer to it around
  8086. if (pshipCredit)
  8087. sprintf(szReason, "%s pushes %s over the top with a kill", pshipCredit->GetName(), psideWin->GetName());
  8088. else
  8089. sprintf(szReason, "%s won by killing enough of their opponents", psideWin->GetName());
  8090. m_pfsMission->GameOver(psideWin, szReason);
  8091. }
  8092. }
  8093. }
  8094. pfmKillShip->typeCredit = type;
  8095. pfmKillShip->idCredit = pcredit->GetObjectID();
  8096. }
  8097. else
  8098. pfmKillShip->typeCredit = pfmKillShip->idCredit = NA;
  8099. g.fm.SendMessages(m_pfsMission->GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  8100. }
  8101. IhullTypeIGC* pht = pside->GetCivilization()->GetLifepod();
  8102. if (!bKilled)
  8103. {
  8104. CFSPlayer* pfsPlayer = pfsShip->GetPlayer();
  8105. pfsPlayer->AddEjection();
  8106. assert (pcluster);
  8107. for (MissileLinkIGC* pml = pcluster->GetMissiles()->first();
  8108. (pml != NULL);
  8109. pml = pml->next())
  8110. {
  8111. ImissileIGC* pmissile = pml->data();
  8112. if (pmissile->GetTarget() == pship)
  8113. pmissile->SetTarget(NULL);
  8114. }
  8115. //Eject the player
  8116. Vector v = pship->GetSourceShip()->GetVelocity();
  8117. //Let our teammates and any observers know that we are now in an eject pod
  8118. pfsShip->ShipStatusHullChange(pht);
  8119. //Put us in the eject pod.
  8120. if (pshipParent)
  8121. pship->SetParentShip(NULL);
  8122. pship->SetBaseHullType(pht);
  8123. Vector f = Vector::RandomDirection();
  8124. Vector position;
  8125. Orientation o;
  8126. if (pshipParent)
  8127. {
  8128. o.Set(f);
  8129. //Start the escape pod well away from the parent ship
  8130. position = p1 - f * (40.0f + 0.5f * pht->GetLength() + pshipParent->GetRadius());
  8131. }
  8132. else
  8133. {
  8134. bool bLook;
  8135. Vector vectorLook;
  8136. if (pcredit)
  8137. {
  8138. ImodelIGC* plauncher = (pcredit->GetObjectType() == OT_ship)
  8139. ? ((IshipIGC*)pcredit)->GetSourceShip()
  8140. : pcredit;
  8141. if (plauncher->GetCluster() == pcluster)
  8142. {
  8143. bLook = true;
  8144. vectorLook = plauncher->GetPosition();
  8145. }
  8146. else
  8147. {
  8148. bLook = false;
  8149. }
  8150. }
  8151. else
  8152. {
  8153. bLook = true;
  8154. vectorLook = p2;
  8155. }
  8156. if (bLook)
  8157. {
  8158. Vector dp = (vectorLook - p1);
  8159. float length2 = dp.LengthSquared();
  8160. if (length2 >= 1.0f)
  8161. {
  8162. o.Set(dp);
  8163. Vector fNew = (f + dp * (8.0f / float(sqrt(length2))));
  8164. length2 = fNew.LengthSquared();
  8165. if (length2 > 0.01f)
  8166. f = fNew / float(sqrt(length2));
  8167. }
  8168. else
  8169. o.Set(f);
  8170. }
  8171. else
  8172. o.Set(f);
  8173. position = p1;
  8174. }
  8175. v -= f * 100.0f;
  8176. pship->SetPosition(position);
  8177. pship->SetVelocity(v);
  8178. pship->SetOrientation(o);
  8179. pship->SetCurrentTurnRate(c_axisRoll, pi * 2.0f);
  8180. pship->SetBB(timeCollision, timeCollision, 0.0f);
  8181. //Send the eject message to everyone in the sector
  8182. {
  8183. BEGIN_PFM_CREATE(g.fm, pfmEject, S, EJECT)
  8184. END_PFM_CREATE
  8185. pfmEject->shipID = shipID;
  8186. pfmEject->position = position;
  8187. pfmEject->velocity = v;
  8188. pfmEject->forward = f;
  8189. pfmEject->cookie = pfsPlayer->NewCookie();
  8190. g.fm.SendMessages(GetGroupSectorFlying(pcluster), FM_GUARANTEED, FM_FLUSH);
  8191. }
  8192. }
  8193. else
  8194. {
  8195. pfsShip->AnnounceExit(NULL, (amount != 0.0f) ? SDR_KILLED : SDR_TERMINATE);
  8196. if (pfsShip->IsPlayer())
  8197. {
  8198. assert (amount != 0.0f);
  8199. assert (pcluster);
  8200. CFSPlayer * pfsPlayer = pfsShip->GetPlayer();
  8201. pfsPlayer->AddDeath();
  8202. IstationIGC* pstationDest = (IstationIGC*)(FindTarget(pship, c_ttFriendly | c_ttStation | c_ttNearest | c_ttAnyCluster,
  8203. NULL, NULL, NULL, NULL,
  8204. c_sabmRestart));
  8205. if (!pstationDest)
  8206. pstationDest = m_pfsMission->GetBase(pside);
  8207. //Oh ick ... if the game ended this tick, the side might not actually have a station
  8208. //so there is no place to send them to. Fortunately, this really is not a problem because
  8209. //if the game ends then the clients are all going to be rather firmly reset anyhow
  8210. //there also might be more than two
  8211. pship->SetCluster(NULL);
  8212. if (pshipParent)
  8213. pship->SetParentShip(NULL);
  8214. assert (pfsShip->GetIGCShip()->GetChildShips()->n() == 0);
  8215. //Put us in the eject pod.
  8216. pship->SetBaseHullType(pht);
  8217. // Look for a new space station for him
  8218. if (pstationDest)
  8219. {
  8220. pfsPlayer->ShipStatusRestart(pstationDest);
  8221. BEGIN_PFM_CREATE(g.fm, pfmEnterLifepod, S, ENTER_LIFEPOD)
  8222. END_PFM_CREATE
  8223. //pfmEnterLifepod->shipID = shipID;
  8224. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  8225. pfsPlayer->GetIGCShip()->SetStation(pstationDest);
  8226. pfsPlayer->ShipStatusDocked(pstationDest);
  8227. pfsPlayer->SetCluster(pcluster, true); //Pretend the client sent a set view cluster
  8228. if (pship->IsGhost() && !m_pfsMission->HasPlayers(pside, false))
  8229. {
  8230. m_pfsMission->DeactivateSide(pside);
  8231. }
  8232. }
  8233. else
  8234. {
  8235. assert (!pside->GetActiveF() || m_pfsMission->GetSideWon());
  8236. }
  8237. }
  8238. else // drone go bye bye
  8239. {
  8240. assert (pship->GetParentShip() == NULL);
  8241. delete pfsShip->GetDrone();
  8242. }
  8243. }
  8244. }
  8245. void FedSrvSiteBase::DamageShipEvent(Time now, IshipIGC* ship, ImodelIGC* launcher, DamageTypeID type, float amount, float leakage, const Vector& p1, const Vector& p2)
  8246. {
  8247. if ((amount >= 1.0f) && launcher && ((type & c_dmgidNoWarn) == 0))
  8248. {
  8249. IsideIGC* psideLauncher = launcher->GetSide();
  8250. if (psideLauncher)
  8251. {
  8252. CFSShip* pcfs = (CFSShip *)(ship->GetPrivateData());
  8253. if (!pcfs->IsPlayer())
  8254. {
  8255. CFSDrone* pd = (CFSDrone*)pcfs;
  8256. //Never report more than once every 10 seconds
  8257. if ((now - pd->GetLastDamageReport()) > 10.0f)
  8258. {
  8259. IsideIGC* pside = ship->GetSide();
  8260. assert (pside);
  8261. if (pside != psideLauncher)
  8262. {
  8263. SoundID underAttackSound;
  8264. switch (ship->GetPilotType())
  8265. {
  8266. case c_ptCarrier:
  8267. underAttackSound = carrierUnderAttackSound;
  8268. break;
  8269. case c_ptMiner:
  8270. if (ship->GetFraction() < 0.5f)
  8271. underAttackSound = minerCriticalSound;
  8272. else
  8273. underAttackSound = minerUnderAttackSound;
  8274. break;
  8275. case c_ptBuilder:
  8276. underAttackSound =
  8277. ((IstationTypeIGC*)(IbaseIGC*)(ship->GetBaseData()))
  8278. ->GetConstructorUnderAttackSound();
  8279. break;
  8280. default:
  8281. case c_ptLayer:
  8282. underAttackSound = droneUnderAttackSound;
  8283. break;
  8284. }
  8285. SendChatf(ship, CHAT_TEAM, ship->GetSide()->GetObjectID(),
  8286. underAttackSound, "Under attack by %s in %s",
  8287. GetModelName(launcher), ship->GetCluster()->GetName());
  8288. }
  8289. else if (launcher->GetObjectType() == OT_ship)
  8290. {
  8291. CFSShip* pfsLauncher = (CFSShip*)(((IshipIGC*)(launcher))->GetPrivateData());
  8292. if (pfsLauncher->IsPlayer())
  8293. {
  8294. SendChat(ship, CHAT_INDIVIDUAL, launcher->GetObjectID(),
  8295. droneWatchFireSound, "Watch your fire");
  8296. }
  8297. }
  8298. pd->SetLastDamageReport(now);
  8299. }
  8300. }
  8301. }
  8302. }
  8303. }
  8304. void FedSrvSiteBase::DamageStationEvent(IstationIGC* station, ImodelIGC* launcher, DamageTypeID type, float amount)
  8305. {
  8306. if ((amount >= 1.0f) && launcher && ((type & c_dmgidNoWarn) == 0))
  8307. {
  8308. IsideIGC* psideLauncher = launcher->GetSide();
  8309. if (psideLauncher)
  8310. {
  8311. if ((g.timeNow - station->GetLastDamageReport()) > 10.0f)
  8312. {
  8313. IsideIGC* pside = station->GetSide();
  8314. if (pside != psideLauncher)
  8315. {
  8316. if (station->GetFraction() < 0.30)
  8317. {
  8318. SendChatf(NULL, CHAT_TEAM, station->GetSide()->GetObjectID(),
  8319. station->GetStationType()->GetCriticalSound(),
  8320. "%s in %s critically damaged and under attack by %s", station->GetName(),
  8321. station->GetCluster()->GetName(), GetModelName(launcher));
  8322. }
  8323. else
  8324. {
  8325. SendChatf(NULL, CHAT_TEAM, station->GetSide()->GetObjectID(),
  8326. station->GetStationType()->GetUnderAttackSound(),
  8327. "%s in %s under attack by %s", station->GetName(),
  8328. station->GetCluster()->GetName(), GetModelName(launcher));
  8329. }
  8330. }
  8331. else if (launcher->GetObjectType() == OT_ship)
  8332. {
  8333. CFSShip* pfsLauncher = (CFSShip*)(((IshipIGC*)(launcher))->GetPrivateData());
  8334. if (pfsLauncher->IsPlayer())
  8335. {
  8336. SendChat(NULL, CHAT_INDIVIDUAL, launcher->GetObjectID(),
  8337. droneWatchFireSound, "Watch your fire");
  8338. }
  8339. }
  8340. station->SetLastDamageReport(g.timeNow);
  8341. }
  8342. }
  8343. }
  8344. }
  8345. void FedSrvSiteBase::KillStationEvent(IstationIGC* pstation, ImodelIGC* plauncher, float amount)
  8346. {
  8347. StationID stationID = pstation->GetObjectID();
  8348. m_pfsMission->VacateStation(pstation);
  8349. ObjectType otLauncher = NA;
  8350. IsideIGC* psideLauncher = NULL;
  8351. if (plauncher)
  8352. {
  8353. otLauncher = plauncher->GetObjectType();
  8354. if (otLauncher == OT_ship)
  8355. {
  8356. IshipIGC* pship = (IshipIGC*)plauncher;
  8357. CFSShip* pfsship = (CFSShip*)(pship->GetPrivateData());
  8358. pfsship->GetPlayerScoreObject()->KillBase(true);
  8359. for (ShipLinkIGC* psl = pship->GetChildShips()->first();
  8360. (psl != NULL);
  8361. psl = psl->next())
  8362. {
  8363. CFSShip* ps = (CFSShip*)(psl->data()->GetPrivateData());
  8364. ps->GetPlayerScoreObject()->KillBase(false);
  8365. }
  8366. }
  8367. psideLauncher = plauncher->GetSide();
  8368. if (psideLauncher)
  8369. {
  8370. psideLauncher->AddBaseKill();
  8371. }
  8372. }
  8373. BEGIN_PFM_CREATE(g.fm, pfmStationDestroyed, S, STATION_DESTROYED)
  8374. END_PFM_CREATE
  8375. pfmStationDestroyed->stationID = stationID;
  8376. pfmStationDestroyed->launcher = (otLauncher == OT_ship) ? plauncher->GetObjectID() : NA;
  8377. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  8378. IsideIGC* pside = pstation->GetSide();
  8379. IsideIGC* psideWin = m_pfsMission->CheckForVictoryByStationKill(pstation, pside);
  8380. if (psideWin)
  8381. {
  8382. static char szReason[100]; //Make this static so we only need to keep a pointer to it around
  8383. if ((otLauncher == OT_ship) && (psideLauncher == psideWin))
  8384. {
  8385. //joeld - added 4th %s and pstation->GetCluster()->GetName() in next line.
  8386. sprintf(szReason, "%s has destroyed %s's %s in %s.", psideLauncher->GetName(), pside->GetName(), pstation->GetName(), pstation->GetCluster()->GetName());
  8387. }
  8388. else
  8389. sprintf(szReason, "%s won by dominating their opponents", psideWin->GetName());
  8390. m_pfsMission->GameOver(psideWin, szReason);
  8391. }
  8392. else if ((otLauncher == OT_ship) && psideLauncher)
  8393. SendChatf(NULL, CHAT_EVERYONE, NA,
  8394. NA, "%s has destroyed %s's %s.",
  8395. psideLauncher->GetName(), pside->GetName(), pstation->GetName());
  8396. pstation->Terminate();
  8397. /*
  8398. //NYI: hack to test simultaneous death
  8399. {
  8400. for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); )
  8401. {
  8402. IshipIGC* pship = psl->data();
  8403. psl = psl->next();
  8404. KillShipEvent(g.timeNow, pship, NULL, 1.0f,
  8405. pship->GetSourceShip()->GetPosition(), Vector::GetZero());
  8406. }
  8407. }
  8408. */
  8409. }
  8410. void FedSrvSiteBase::KillAsteroidEvent(IasteroidIGC* pasteroid, bool explodeF)
  8411. {
  8412. BEGIN_PFM_CREATE(g.fm, pfmAsteroidDestroyed, S, ASTEROID_DESTROYED)
  8413. END_PFM_CREATE
  8414. pfmAsteroidDestroyed->clusterID = pasteroid->GetCluster()->GetObjectID();
  8415. pfmAsteroidDestroyed->asteroidID = pasteroid->GetObjectID();
  8416. pfmAsteroidDestroyed->explodeF = explodeF;
  8417. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  8418. pasteroid->Terminate();
  8419. }
  8420. void FedSrvSiteBase::DrainAsteroidEvent(IasteroidIGC* pasteroid)
  8421. {
  8422. BEGIN_PFM_CREATE(g.fm, pfmAsteroidDrained, S, ASTEROID_DRAINED)
  8423. END_PFM_CREATE
  8424. pfmAsteroidDrained->clusterID = pasteroid->GetCluster()->GetObjectID();
  8425. pfmAsteroidDrained->asteroidID = pasteroid->GetObjectID();
  8426. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  8427. pasteroid->SetOre(0.0f);
  8428. }
  8429. void FedSrvSiteBase::KillProbeEvent(IprobeIGC* pprobe)
  8430. {
  8431. BEGIN_PFM_CREATE(g.fm, pfmProbeDestroyed, S, PROBE_DESTROYED)
  8432. END_PFM_CREATE
  8433. pfmProbeDestroyed->clusterID = pprobe->GetCluster()->GetObjectID();
  8434. pfmProbeDestroyed->probeID = pprobe->GetObjectID();
  8435. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  8436. pprobe->Terminate();
  8437. }
  8438. void FedSrvSiteBase::KillBuildingEffectEvent(IbuildingEffectIGC* pbe)
  8439. {
  8440. BEGIN_PFM_CREATE(g.fm, pfmBuildingEffectDestroyed, S, BUILDINGEFFECT_DESTROYED)
  8441. END_PFM_CREATE
  8442. pfmBuildingEffectDestroyed->asteroidID = pbe->GetAsteroid()->GetObjectID();
  8443. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  8444. pbe->Terminate();
  8445. }
  8446. void FedSrvSiteBase::KillTreasureEvent(ItreasureIGC* ptreasure)
  8447. {
  8448. if (m_pfsMission && m_pfsMission->GetIGCMission()->GetMissionStage() == STAGE_STARTED)
  8449. {
  8450. //No more treasure ... spread the word
  8451. BEGIN_PFM_CREATE(g.fm, pfmDT, S, DESTROY_TREASURE)
  8452. END_PFM_CREATE
  8453. pfmDT->treasureID = ptreasure->GetObjectID();
  8454. pfmDT->sectorID = ptreasure->GetCluster()->GetObjectID();
  8455. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_NOT_GUARANTEED, FM_FLUSH);
  8456. if (ptreasure->GetTreasureCode() == c_tcFlag)
  8457. RespawnFlag(ptreasure->GetTreasureID());
  8458. }
  8459. ptreasure->Terminate();
  8460. }
  8461. void FedSrvSiteBase::KillMissileEvent(ImissileIGC* pmissile, const Vector& position)
  8462. {
  8463. BEGIN_PFM_CREATE(g.fm, pfmMissileDestroyed, S, MISSILE_DESTROYED)
  8464. END_PFM_CREATE
  8465. pfmMissileDestroyed->clusterID = pmissile->GetCluster()->GetObjectID();
  8466. pfmMissileDestroyed->missileID = pmissile->GetObjectID();
  8467. pfmMissileDestroyed->position = position;
  8468. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  8469. pmissile->Terminate();
  8470. }
  8471. void FedSrvSiteBase::KillMineEvent(ImineIGC* pmine)
  8472. {
  8473. BEGIN_PFM_CREATE(g.fm, pfmMineDestroyed, S, MINE_DESTROYED)
  8474. END_PFM_CREATE
  8475. pfmMineDestroyed->clusterID = pmine->GetCluster()->GetObjectID();
  8476. pfmMineDestroyed->mineID = pmine->GetObjectID();
  8477. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  8478. pmine->Terminate();
  8479. }
  8480. void FedSrvSiteBase::WarpBombEvent(IwarpIGC* pwarp, ImissileIGC* pmissile)
  8481. {
  8482. assert (pwarp);
  8483. assert (pmissile);
  8484. ImissileTypeIGC* pmt = pmissile->GetMissileType();
  8485. assert (pmt);
  8486. assert (pmt->GetObjectType() == OT_missileType);
  8487. Time timeExplosion = pwarp->GetLastUpdate() + m_pfsMission->GetIGCMission()->GetFloatConstant(c_fcidWarpBombDelay);
  8488. {
  8489. BEGIN_PFM_CREATE(g.fm, pfmWB, S, WARP_BOMB)
  8490. END_PFM_CREATE
  8491. pfmWB->timeExplosion = timeExplosion;
  8492. pfmWB->warpidBombed = pwarp->GetObjectID();
  8493. pfmWB->expendableidMissile = pmt->GetObjectID();
  8494. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  8495. }
  8496. pwarp->AddBomb(timeExplosion, pmt);
  8497. /*
  8498. DamageTypeID dtid = pmt->GetDamageType();
  8499. float p = pmt->GetPower();
  8500. float r = pmt->GetBlastRadius();
  8501. IclusterIGC* pcluster = pwarp->GetCluster();
  8502. pcluster->CreateExplosion(dtid,
  8503. p,
  8504. r,
  8505. c_etBigShip,
  8506. pcluster->GetLastUpdate(),
  8507. pwarp->GetPosition(),
  8508. NULL);
  8509. pwarp = pwarp->GetDestination();
  8510. pcluster = pwarp->GetCluster();
  8511. pcluster->CreateExplosion(dtid,
  8512. p,
  8513. r,
  8514. c_etBigShip,
  8515. pcluster->GetLastUpdate(),
  8516. pwarp->GetPosition(),
  8517. NULL);
  8518. */
  8519. }
  8520. static void RefillPart(IshipIGC* pship, IpartIGC* ppart, const TechTreeBitMask& ttbm)
  8521. {
  8522. IpartTypeIGC* pptBase = ppart->GetPartType();
  8523. if (pptBase->GetRequiredTechs() <= ttbm)
  8524. {
  8525. IpartTypeIGC* ppt = pptBase;
  8526. while (true)
  8527. {
  8528. IpartTypeIGC* pptNext = ppt->GetSuccessorPartType();
  8529. if (pptNext && (pptNext->GetRequiredTechs() <= ttbm))
  8530. ppt = pptNext;
  8531. else
  8532. break;
  8533. }
  8534. short newAmount = (ppart->GetPrice() == 0)
  8535. ? SHRT_MAX
  8536. : ppart->GetAmount();
  8537. if (ppt == pptBase)
  8538. {
  8539. //Things that don't care about amounts wont care if I try and set it.
  8540. ppart->SetAmount(newAmount);
  8541. }
  8542. else
  8543. {
  8544. Mount oldMount = ppart->GetMountID();
  8545. ppart->Terminate();
  8546. pship->CreateAndAddPart(ppt, oldMount, newAmount);
  8547. }
  8548. }
  8549. }
  8550. static bool MatchPT(IpartTypeIGC* ppt1, IpartTypeIGC* ppt2)
  8551. {
  8552. do
  8553. {
  8554. if (ppt1 == ppt2)
  8555. return true;
  8556. ppt1 = ppt1->GetSuccessorPartType();
  8557. }
  8558. while (ppt1);
  8559. return false;
  8560. }
  8561. static void ErasePart(IshipIGC* pship, EquipmentType et, Mount mount, IpartTypeIGC* ptDesired[c_maxCargo + 3])
  8562. {
  8563. IpartIGC* ppart = pship->GetMountedPart(et, mount);
  8564. if (ppart)
  8565. {
  8566. IpartTypeIGC* ppt = ppart->GetPartType();
  8567. //Go through the list of parts and nuke one that corresponds to this part
  8568. for (int i = 0; (i < c_maxCargo + 3); i++)
  8569. {
  8570. if (ptDesired[i] && MatchPT(ptDesired[i], ppt))
  8571. {
  8572. ptDesired[i] = NULL;
  8573. break;
  8574. }
  8575. }
  8576. }
  8577. }
  8578. bool FedSrvSiteBase::LandOnCarrierEvent(IshipIGC* pshipCarrier, IshipIGC* pship, float tCollision)
  8579. {
  8580. CFSPlayer* pfsPlayer = ((CFSShip*)(pship->GetPrivateData()))->GetPlayer();
  8581. ImissionIGC* pMission = pship->GetMission();
  8582. TechTreeBitMask ttbm = pship->GetSide()->GetDevelopmentTechs() | pship->GetSide()->GetBuildingTechs();
  8583. //Now ... go through the players ship and restore the expendables that are
  8584. //free and partially gone
  8585. {
  8586. //refill parts backwards so that newly added parts will not be revisited
  8587. PartLinkIGC* ppl = pship->GetParts()->last();
  8588. while (ppl)
  8589. {
  8590. IpartIGC* ppart = ppl->data();
  8591. ppl = ppl->txen();
  8592. if (ppart)
  8593. RefillPart(pship, ppart, ttbm);
  8594. }
  8595. }
  8596. //Copy the list of desired parts
  8597. IpartTypeIGC* ptDesired[c_maxCargo + 3]; //Must be kept sync'd with
  8598. memcpy(ptDesired, pfsPlayer->GetDesiredLoadout(), sizeof(ptDesired));
  8599. //Go through and fill any holes with the desired parts
  8600. {
  8601. ErasePart(pship, ET_Magazine, 0, ptDesired);
  8602. ErasePart(pship, ET_Dispenser, 0, ptDesired);
  8603. ErasePart(pship, ET_ChaffLauncher, 0, ptDesired);
  8604. for (Mount i = 0; (i < c_maxCargo); i++)
  8605. ErasePart(pship, NA, -1 - i, ptDesired);
  8606. }
  8607. {
  8608. IpartTypeIGC* ptFuel = (pship->GetMountedPart(ET_Afterburner, 0) != NULL)
  8609. ? pMission->GetFuelPack()
  8610. : NULL;
  8611. IpartTypeIGC* ptAmmo = NULL;
  8612. {
  8613. for (Mount i = pship->GetHullType()->GetMaxWeapons() - 1; (i >= 0); i--)
  8614. {
  8615. IweaponIGC* pw = (IweaponIGC*)(pship->GetMountedPart(ET_Weapon, i));
  8616. if (pw && (pw->GetAmmoPerShot() != 0))
  8617. {
  8618. ptAmmo = pMission->GetAmmoPack();
  8619. break;
  8620. }
  8621. }
  8622. }
  8623. bool bBuyFuel = true;
  8624. int iDesired = 0;
  8625. for (Mount i = 0; (i < c_maxCargo); i++)
  8626. {
  8627. IpartIGC* ppart = pship->GetMountedPart(NA, -1 - i);
  8628. if (ppart == NULL)
  8629. {
  8630. //Shove an instance of the first part left in the
  8631. //desired part list into the slot
  8632. while (iDesired < c_maxCargo + 3)
  8633. {
  8634. IpartTypeIGC* pt = ptDesired[iDesired++];
  8635. if (pt && (pt->GetRequiredTechs() <= ttbm))
  8636. {
  8637. while (true)
  8638. {
  8639. IpartTypeIGC* ptNext = pt->GetSuccessorPartType();
  8640. if (ptNext && (ptNext->GetRequiredTechs() <= ttbm))
  8641. pt = ptNext;
  8642. else
  8643. break;
  8644. }
  8645. ppart = pship->CreateAndAddPart(pt, -1 - i, SHRT_MAX);
  8646. assert (ppart->GetPrice() == 0);
  8647. break;
  8648. }
  8649. }
  8650. if (ppart == NULL)
  8651. {
  8652. if (bBuyFuel && ptFuel)
  8653. {
  8654. pship->CreateAndAddPart(ptFuel, -1 - i, SHRT_MAX);
  8655. bBuyFuel = (ptAmmo == NULL);
  8656. }
  8657. else if (ptAmmo)
  8658. {
  8659. pship->CreateAndAddPart(ptAmmo, -1 - i, SHRT_MAX);
  8660. bBuyFuel = true;
  8661. }
  8662. }
  8663. }
  8664. }
  8665. }
  8666. pship->SetAmmo(SHRT_MAX);
  8667. pship->SetFuel(FLT_MAX);
  8668. pship->SetEnergy(pship->GetHullType()->GetMaxEnergy());
  8669. pship->SetFraction(1.0f);
  8670. {
  8671. IshieldIGC* pshield = (IshieldIGC*)(pship->GetMountedPart(ET_Shield, 0));
  8672. if (pshield)
  8673. pshield->SetFraction(1.0f);
  8674. }
  8675. const Orientation& orientation = pshipCarrier->GetOrientation();
  8676. float vLaunch = pMission->GetFloatConstant(c_fcidExitStationSpeed);
  8677. Vector position = pshipCarrier->GetPosition();
  8678. Vector velocity = pshipCarrier->GetVelocity();
  8679. position.x += random(-0.5f, 0.5f);
  8680. position.y += random(-0.5f, 0.5f);
  8681. position.z += random(-0.5f, 0.5f);
  8682. IhullTypeIGC* phtCarrier = pshipCarrier->GetBaseHullType();
  8683. Orientation orientationBfr;
  8684. const Orientation* porientation;
  8685. if (phtCarrier->GetLaunchSlots() == 0)
  8686. {
  8687. position -= orientation.GetBackward() * (pshipCarrier->GetRadius() + pship->GetRadius() + vLaunch * 0.5f);
  8688. velocity -= orientation.GetBackward() * vLaunch;
  8689. porientation = &orientation;
  8690. }
  8691. else
  8692. {
  8693. short slot = pshipCarrier->GetLaunchSlot();
  8694. position += phtCarrier->GetLaunchPosition(slot) * orientation;
  8695. Vector forward = phtCarrier->GetLaunchDirection(slot) * orientation;
  8696. position += forward * (pship->GetRadius() + vLaunch * 0.5f);
  8697. velocity += forward * vLaunch;
  8698. orientationBfr = orientation;
  8699. orientationBfr.TurnTo(forward);
  8700. porientation = &orientationBfr;
  8701. }
  8702. pship->SetPosition(position);
  8703. pship->SetVelocity(velocity);
  8704. pship->SetOrientation(*porientation);
  8705. IclusterIGC* pcluster = pship->GetCluster();
  8706. pcluster->RecalculateCollisions(tCollision, pship, NULL);
  8707. //Send the launch message to everyone in the sector
  8708. {
  8709. BEGIN_PFM_CREATE(g.fm, pfmLaunch, S, RELAUNCH_SHIP)
  8710. FM_VAR_PARM(NULL, pship->ExportShipLoadout(NULL))
  8711. END_PFM_CREATE
  8712. pship->ExportShipLoadout((ShipLoadout*)FM_VAR_REF(pfmLaunch, loadout));
  8713. pfmLaunch->shipID = pship->GetObjectID();
  8714. pfmLaunch->carrierID = pshipCarrier->GetObjectID();
  8715. pfmLaunch->position = position;
  8716. pfmLaunch->velocity = velocity;
  8717. pfmLaunch->orientation.Set(*porientation);
  8718. pfmLaunch->cookie = pfsPlayer->NewCookie();
  8719. g.fm.SendMessages(GetGroupSectorFlying(pship->GetCluster()), FM_GUARANTEED, FM_FLUSH);
  8720. }
  8721. return true;
  8722. }
  8723. bool FedSrvSiteBase::RescueShipEvent(IshipIGC* pshipRescued, IshipIGC* pshipRescuer)
  8724. {
  8725. assert (pshipRescued);
  8726. if (pshipRescuer)
  8727. {
  8728. CFSShip* pfsShip = (CFSShip*)(pshipRescuer->GetPrivateData());
  8729. pfsShip->GetPlayerScoreObject()->AddRescue();
  8730. BEGIN_PFM_CREATE(g.fm, pfmPlayerRescued, S, PLAYER_RESCUED)
  8731. END_PFM_CREATE
  8732. pfmPlayerRescued->shipIDRescuer = pshipRescuer->GetObjectID();
  8733. pfmPlayerRescued->shipIDRescuee = pshipRescued->GetObjectID();
  8734. if (pfsShip->IsPlayer())
  8735. g.fm.SendMessages(pfsShip->GetPlayer()->GetConnection(), FM_GUARANTEED, FM_DONT_FLUSH);
  8736. g.fm.SendMessages(((CFSShip*)(pshipRescued->GetPrivateData()))->GetPlayer()->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  8737. }
  8738. ((CFSShip*)(pshipRescued->GetPrivateData()))->ResetWarpState();
  8739. //Find the closest space station and put the rescued ship there
  8740. IstationIGC* pstation = (IstationIGC*)FindTarget(pshipRescued,
  8741. (c_ttStation | c_ttFriendly | c_ttAnyCluster | c_ttNearest),
  8742. NULL, NULL, NULL, NULL, c_sabmRestart);
  8743. return pstation ? DockWithStationEvent(pshipRescued, pstation) : false;
  8744. }
  8745. void FedSrvSiteBase::CaptureStationEvent(IshipIGC* pship, IstationIGC* pstation)
  8746. {
  8747. CFSShip* pfsShip = (CFSShip *) pship->GetPrivateData();
  8748. pfsShip->CaptureStation(pstation);
  8749. }
  8750. bool FedSrvSiteBase::DockWithStationEvent(IshipIGC* pship, IstationIGC* pstation)
  8751. {
  8752. assert (pship->GetParentShip() == NULL);
  8753. CFSShip* pfsShip = (CFSShip *) pship->GetPrivateData();
  8754. if (!pfsShip->OkToWarp())
  8755. return false;
  8756. IsideIGC* pside = pship->GetSide();
  8757. IsideIGC* psideOld = pstation->GetSide();
  8758. if (pside != psideOld)
  8759. {
  8760. pfsShip->CaptureStation(pstation);
  8761. }
  8762. pfsShip->AnnounceExit(pship->GetCluster(), SDR_DOCKED);
  8763. // let anyone who can see us know that we are docked
  8764. pfsShip->ShipStatusDocked(pstation);
  8765. {
  8766. SideID sidFlag = pship->GetFlag();
  8767. if (sidFlag != NA)
  8768. {
  8769. if (sidFlag == SIDE_TEAMLOBBY)
  8770. {
  8771. pfsShip->GetPlayerScoreObject()->AddArtifact();
  8772. pside->SetArtifacts(pside->GetArtifacts() + 1);
  8773. SendChatf(NULL, CHAT_TEAM, pside->GetObjectID(),
  8774. artifactSecuredSound, "%s has secured an artifact.",
  8775. pship->GetName());
  8776. }
  8777. else
  8778. {
  8779. pfsShip->GetPlayerScoreObject()->AddFlag();
  8780. pside->SetFlags(pside->GetFlags() + 1);
  8781. SendChatf(NULL, CHAT_TEAM, pside->GetObjectID(),
  8782. enemyFlagSecuredSound, "%s has secured %s's flag.",
  8783. pship->GetName(), pfsShip->GetIGCShip()->GetMission()->GetSide(sidFlag)->GetName());
  8784. SendChatf(NULL, CHAT_TEAM, sidFlag,
  8785. flagSecuredSound, "%s of %s has secured your flag.",
  8786. pship->GetName(), pside->GetName ());
  8787. }
  8788. pship->SetFlag(NA);
  8789. BEGIN_PFM_CREATE(g.fm, pfmGain, S, GAIN_FLAG)
  8790. END_PFM_CREATE
  8791. pfmGain->sideidFlag = NA;
  8792. pfmGain->shipidRecipient = pship->GetObjectID();
  8793. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  8794. IsideIGC* psideWin = m_pfsMission->CheckForVictoryByFlags(pside, sidFlag);
  8795. if (psideWin)
  8796. {
  8797. static char szReason[100]; //Make this static so we only need to keep a pointer to it around
  8798. sprintf(szReason,
  8799. (sidFlag != NA)
  8800. ? "%s wins with the flag recovered by %s"
  8801. : "%s wins with the artifact recovered by %s",
  8802. psideWin->GetName(), pship->GetName());
  8803. m_pfsMission->GameOver(psideWin, szReason);
  8804. }
  8805. RespawnFlag(sidFlag);
  8806. }
  8807. }
  8808. pship->SetStation(pstation);
  8809. {
  8810. //Move the children of this ship to the station as well
  8811. for (ShipLinkIGC* psl = pship->GetChildShips()->first();
  8812. (psl != NULL);
  8813. psl = psl->next())
  8814. {
  8815. psl->data()->SetStation(pstation);
  8816. }
  8817. }
  8818. return true;
  8819. }
  8820. void FedSrvSiteBase::ChangeStation(IshipIGC* pship, IstationIGC* pstationOld, IstationIGC* pstationNew)
  8821. {
  8822. assert (pship);
  8823. CFSShip * pfsShip = (CFSShip *) pship->GetPrivateData();
  8824. if (pstationNew == NULL)
  8825. {
  8826. //Let the parent handle the launch & only allow launched in the game.
  8827. if ((m_pfsMission->GetStage() == STAGE_STARTED) && (pship->GetParentShip() == NULL))
  8828. {
  8829. pstationOld->RepairAndRefuel(pship);
  8830. pfsShip->Launch(pstationOld);
  8831. }
  8832. }
  8833. else
  8834. {
  8835. if (pstationOld == NULL)
  8836. {
  8837. //Go over the ship's parts and give the side the tech bits for any equipment onboard
  8838. TechTreeBitMask ttbm;
  8839. ttbm.ClearAll();
  8840. for (PartLinkIGC* ppl = pship->GetParts()->first(); (ppl != NULL); ppl = ppl->next())
  8841. {
  8842. IpartIGC* ppart = ppl->data();
  8843. ttbm |= ppart->GetPartType()->GetEffectTechs();
  8844. }
  8845. IsideIGC* pside = pship->GetSide();
  8846. assert (pside);
  8847. if (pside->ApplyDevelopmentTechs(ttbm))
  8848. pfsShip->GetPlayerScoreObject()->AddTechsRecovered();
  8849. }
  8850. pfsShip->Dock(pstationNew);
  8851. if (!pfsShip->IsPlayer())
  8852. UpgradeShip(pship, pstationNew);
  8853. }
  8854. }
  8855. void * FedSrvSiteBase::GetDroneFromShip(IshipIGC * pship) // return value is really a Drone*
  8856. {
  8857. CFSShip * pfsShip = (CFSShip *) pship->GetPrivateData();
  8858. return pfsShip->IsPlayer() ? NULL : (void *) pfsShip->GetDrone()->GetDrone();
  8859. }
  8860. void FedSrvSiteBase::RequestRipcord(IshipIGC* pship, IclusterIGC* pcluster)
  8861. {
  8862. IclusterIGC* pclusterShip = pship->GetCluster();
  8863. CFSShip* pfsShip = (CFSShip *)pship->GetPrivateData();
  8864. CFSMission* pfsMission = pfsShip->GetMission();
  8865. ImodelIGC* pmodelRipcord = pship->GetRipcordModel();
  8866. if (pcluster == NULL)
  8867. {
  8868. if (pmodelRipcord != NULL)
  8869. {
  8870. assert (pclusterShip);
  8871. pship->SetRipcordModel(NULL);
  8872. BEGIN_PFM_CREATE(g.fm, pfmRipcordAborted, S, RIPCORD_ABORTED)
  8873. END_PFM_CREATE
  8874. pfmRipcordAborted->shipidRipcord = pship->GetObjectID();
  8875. g.fm.SendMessages(GetGroupSectorFlying(pclusterShip), FM_GUARANTEED, FM_FLUSH);
  8876. }
  8877. }
  8878. else if ((pmodelRipcord == NULL) ||
  8879. (pmodelRipcord->GetCluster() != pcluster))
  8880. {
  8881. assert (pclusterShip);
  8882. ImodelIGC* pmodelNew = pship->FindRipcordModel(pcluster);
  8883. if (pmodelNew)
  8884. {
  8885. ImodelIGC* pmodelOld = pship->GetRipcordModel();
  8886. if (pmodelNew != pmodelOld)
  8887. {
  8888. pship->SetRipcordModel(pmodelNew);
  8889. pship->ResetRipcordTimeLeft();
  8890. }
  8891. if ((pmodelNew != pmodelOld) && pfsShip->IsPlayer())
  8892. {
  8893. BEGIN_PFM_CREATE(g.fm, pfmRA, S, RIPCORD_ACTIVATE)
  8894. END_PFM_CREATE
  8895. pfmRA->shipidRipcord = pship->GetObjectID();
  8896. pfmRA->otRipcord = pmodelNew->GetObjectType();
  8897. pfmRA->oidRipcord = pmodelNew->GetObjectID();
  8898. pfmRA->sidRipcord = pcluster->GetObjectID();
  8899. g.fm.SendMessages((pmodelOld == NULL)
  8900. ? (CFMRecipient*)(GetGroupSectorFlying(pclusterShip))
  8901. : (CFMRecipient*)(pfsShip->GetPlayer()->GetConnection()), FM_GUARANTEED, FM_FLUSH);
  8902. }
  8903. }
  8904. else if (pfsShip->IsPlayer())
  8905. {
  8906. BEGIN_PFM_CREATE(g.fm, pfmRipcordDenied, S, RIPCORD_DENIED)
  8907. END_PFM_CREATE
  8908. g.fm.SendMessages(pfsShip->GetPlayer()->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  8909. }
  8910. }
  8911. }
  8912. bool FedSrvSiteBase::ContinueRipcord(IshipIGC* pship, ImodelIGC* pmodel)
  8913. {
  8914. bool rc;
  8915. if ((pship->GetFlag() != NA) ||
  8916. (pship->GetSide() != pmodel->GetSide()) ||
  8917. (pmodel->GetCluster() == NULL))
  8918. {
  8919. rc = false;
  8920. }
  8921. else if (pmodel->GetObjectType() == OT_ship)
  8922. {
  8923. IshipIGC* ps = (IshipIGC*)pmodel;
  8924. IhullTypeIGC* pht = ps->GetBaseHullType();
  8925. rc = pht &&
  8926. pht->HasCapability(pship->GetBaseHullType()->HasCapability(c_habmCanLtRipcord)
  8927. ? (c_habmIsRipcordTarget | c_habmIsLtRipcordTarget)
  8928. : c_habmIsRipcordTarget);
  8929. }
  8930. else
  8931. rc = true;
  8932. if (!rc)
  8933. {
  8934. BEGIN_PFM_CREATE(g.fm, pfmRipcordAborted, S, RIPCORD_ABORTED)
  8935. END_PFM_CREATE
  8936. pfmRipcordAborted->shipidRipcord = pship->GetObjectID();
  8937. g.fm.SendMessages(GetGroupSectorFlying(pship->GetCluster()), FM_GUARANTEED, FM_FLUSH);
  8938. }
  8939. return rc;
  8940. }
  8941. bool FedSrvSiteBase::UseRipcord(IshipIGC* pship, ImodelIGC* pmodel)
  8942. {
  8943. assert (pmodel);
  8944. ObjectType type = pmodel->GetObjectType();
  8945. if (type == OT_station)
  8946. ((IstationIGC*)pmodel)->Launch(pship);
  8947. else
  8948. {
  8949. if (type == OT_ship)
  8950. {
  8951. float newEnergy = ((IshipIGC*)pmodel)->GetEnergy() - pship->GetBaseHullType()->GetRipcordCost();
  8952. if (newEnergy < 0.0f)
  8953. {
  8954. //No energy for ripcord
  8955. BEGIN_PFM_CREATE(g.fm, pfmRipcordAborted, S, RIPCORD_ABORTED)
  8956. END_PFM_CREATE
  8957. pfmRipcordAborted->shipidRipcord = pship->GetObjectID();
  8958. g.fm.SendMessages(GetGroupSectorFlying(pship->GetCluster()), FM_GUARANTEED, FM_FLUSH);
  8959. return true;
  8960. }
  8961. else
  8962. ((IshipIGC*)pmodel)->SetEnergy(newEnergy);
  8963. }
  8964. float r = pmodel->GetRadius() + pship->GetRadius() + 25.0f;
  8965. Vector v = Vector::RandomDirection();
  8966. Orientation o(v);
  8967. IclusterIGC* pcluster = pmodel->GetCluster();
  8968. Time lastUpdate = pcluster->GetLastUpdate();
  8969. pship->SetPosition(pmodel->GetPosition() + v * r);
  8970. pship->SetVelocity(v * m_pfsMission->GetIGCMission()->GetFloatConstant(c_fcidExitStationSpeed));
  8971. pship->SetOrientation(o);
  8972. pship->SetCurrentTurnRate(c_axisYaw, 0.0f);
  8973. pship->SetCurrentTurnRate(c_axisPitch, 0.0f);
  8974. pship->SetCurrentTurnRate(c_axisRoll, 0.0f);
  8975. pship->SetCluster(pcluster);
  8976. if ((type == OT_probe) && (randomInt(0, 5) == 0))
  8977. {
  8978. KillProbeEvent((IprobeIGC*)pmodel);
  8979. //pmodel->Terminate();
  8980. }
  8981. }
  8982. return true;
  8983. }
  8984. void FedSrvSiteBase::HitWarpEvent(IshipIGC* pship, IwarpIGC* pwarp)
  8985. {
  8986. CFSShip * pfsShip = (CFSShip *) pship->GetPrivateData();
  8987. pfsShip->HitWarp(pwarp);
  8988. }
  8989. void FedSrvSiteBase::ChangeCluster(IshipIGC* pship,
  8990. IclusterIGC* pclusterOld,
  8991. IclusterIGC* pclusterNew)
  8992. {
  8993. CFSShip* pfsShip = (CFSShip *) pship->GetPrivateData();
  8994. //Only announce my exit from the old cluster if going from one cluster to another
  8995. //If going from a valid cluster, then
  8996. // I'm either docking (and an announce exit is sent by the dock event)
  8997. // I'm dying (and an announce exit is sent by the thing that killed me)
  8998. // I'm leaving the game (ditto)
  8999. if ((pclusterOld && pclusterNew) && (pclusterOld != pclusterNew))
  9000. pfsShip->AnnounceExit(pclusterOld, SDR_LEFTSECTOR);
  9001. pfsShip->SetCluster(pclusterNew);
  9002. }
  9003. void FedSrvSiteBase::LoadoutChangeEvent(IshipIGC* pship, IpartIGC* ppart, LoadoutChange lc)
  9004. {
  9005. // if we are mounting or dismounting an area effect weapon...
  9006. if (lc == c_lcAdded && ppart->GetEquipmentType() == ET_Weapon
  9007. && ((IweaponIGC*)ppart)->GetProjectileType()->GetBlastRadius() > 0.0f)
  9008. {
  9009. // REVIEW: we could add a check to see if there was already an area effect weapon mounted,
  9010. // or even cache the last target that was broadcast. It's probably not worth maintaining
  9011. // the extra code, however.
  9012. // ...resend the current target
  9013. CommandChangedEvent(c_cmdCurrent, pship,
  9014. pship->GetCommandTarget(c_cmdCurrent),
  9015. pship->GetCommandID(c_cmdCurrent));
  9016. }
  9017. /*
  9018. if (lc != c_lcTurretChange)
  9019. {
  9020. CFSShip* pfsShip = (CFSShip *) pship->GetPrivateData();
  9021. if (pfsShip)
  9022. {
  9023. pfsShip->ShipLoadoutChange(lc);
  9024. }
  9025. }
  9026. */
  9027. }
  9028. bool FedSrvSiteBase::HitTreasureEvent(Time now, IshipIGC* ship, ItreasureIGC* treasure)
  9029. {
  9030. TreasureCode tc = treasure->GetTreasureCode();
  9031. ObjectID oid = treasure->GetTreasureID();
  9032. if (tc == c_tcFlag)
  9033. {
  9034. //Can only carry a single flag
  9035. if ((ship->GetFlag() != NA) || (oid == ship->GetSide()->GetObjectID()))
  9036. {
  9037. return false;
  9038. }
  9039. BEGIN_PFM_CREATE(g.fm, pfmGain, S, GAIN_FLAG)
  9040. END_PFM_CREATE
  9041. pfmGain->sideidFlag = oid;
  9042. pfmGain->shipidRecipient = ship->GetObjectID();
  9043. g.fm.SendMessages(m_pfsMission->GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  9044. //Clear the flag so it does not respawn
  9045. treasure->SetTreasureID(NA);
  9046. }
  9047. short amount = treasure->GetAmount();
  9048. if (tc == c_tcPart)
  9049. {
  9050. // if this was a player, the message below will take care of it.
  9051. // REVIEW: shouldn't drones pick up treasure if they can?
  9052. }
  9053. else if (tc == c_tcCash)
  9054. {
  9055. CFSShip* pfsShip = (CFSShip*)(ship->GetPrivateData());
  9056. if (pfsShip->IsPlayer())
  9057. {
  9058. IshipIGC* pshipDonate = ship->GetAutoDonate();
  9059. if (pshipDonate == NULL)
  9060. pshipDonate = ship;
  9061. CFSShip* pfsDonate = (CFSShip*)(pshipDonate->GetPrivateData());
  9062. assert (pfsDonate->IsPlayer());
  9063. assert (amount >= 0);
  9064. BEGIN_PFM_CREATE(g.fm, pfmMoney, S, MONEY_CHANGE)
  9065. END_PFM_CREATE
  9066. pfmMoney->sidTo = pshipDonate->GetObjectID();
  9067. pfmMoney->sidFrom = NA;
  9068. pfmMoney->dMoney = amount;
  9069. pfsDonate->SetMoney(pfsDonate->GetMoney() + amount);
  9070. g.fm.SendMessages(CFSSide::FromIGC(pfsShip->GetSide())->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  9071. }
  9072. else
  9073. {
  9074. m_pfsMission->GiveSideMoney(ship->GetSide(), Money(amount));
  9075. }
  9076. }
  9077. else
  9078. ship->HitTreasure(tc, oid, amount);
  9079. CFSShip* pfsShip = (CFSShip*)(ship->GetPrivateData());
  9080. if (pfsShip->IsPlayer())
  9081. {
  9082. CFSPlayer* pfsPlayer = pfsShip->GetPlayer();
  9083. //As long as either it isn't a part or there is space in the treasure buffer
  9084. if ((tc != c_tcPart) || (pfsPlayer->GetTreasureObjectID() == NA))
  9085. {
  9086. //Tell the player they got it & let them deal with it.
  9087. if (tc == c_tcPart)
  9088. pfsPlayer->SetTreasureData(oid, amount);
  9089. BEGIN_PFM_CREATE(g.fm, pfmAT, S, ACQUIRE_TREASURE)
  9090. END_PFM_CREATE
  9091. pfmAT->treasureCode = tc;
  9092. pfmAT->treasureID = oid;
  9093. pfmAT->amount = amount;
  9094. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  9095. }
  9096. }
  9097. return true;
  9098. }
  9099. void FedSrvSiteBase::PaydayEvent(IsideIGC* pside, float money)
  9100. {
  9101. m_pfsMission->GiveSideMoney(pside, Money(money));
  9102. }
  9103. void FedSrvSiteBase::SendChat(IshipIGC* pshipSender,
  9104. ChatTarget chatTarget,
  9105. ObjectID oidRecipient,
  9106. SoundID soVoiceOver,
  9107. const char* pszText,
  9108. CommandID cid,
  9109. ObjectType otTarget,
  9110. ObjectID oidTarget,
  9111. ImodelIGC* pmodelTarget,
  9112. bool bObjectModel)
  9113. {
  9114. BEGIN_PFM_CREATE_ALLOC(g.fm, pfmChatAlloc, CS, CHATMESSAGE)
  9115. FM_VAR_PARM(pszText, CB_ZTS)
  9116. END_PFM_CREATE
  9117. pfmChatAlloc->cd.sidSender = pshipSender ? pshipSender->GetObjectID()
  9118. : NA;
  9119. pfmChatAlloc->cd.chatTarget = chatTarget;
  9120. pfmChatAlloc->cd.oidRecipient = oidRecipient;
  9121. pfmChatAlloc->cd.commandID = cid;
  9122. pfmChatAlloc->cd.voiceOver = soVoiceOver;
  9123. pfmChatAlloc->cd.bObjectModel = bObjectModel;
  9124. pfmChatAlloc->otTarget = otTarget;
  9125. pfmChatAlloc->oidTarget = oidTarget;
  9126. ForwardChatMessage(m_pfsMission,
  9127. pshipSender
  9128. ? (CFSShip*)(pshipSender->GetPrivateData())
  9129. : NULL,
  9130. pfmChatAlloc,
  9131. &(pfmChatAlloc->cd),
  9132. pszText,
  9133. otTarget,
  9134. oidTarget,
  9135. NULL);
  9136. PFM_DEALLOC(pfmChatAlloc);
  9137. }
  9138. void FedSrvSiteBase::ClusterUpdateEvent(IclusterIGC* c)
  9139. {
  9140. }
  9141. void FedSrvSiteBase::CommandChangedEvent(Command i, IshipIGC * pship, ImodelIGC* ptarget, CommandID cid)
  9142. {
  9143. // forward the command change to everyone on the side.
  9144. IclusterIGC* pcluster;
  9145. if ((i == c_cmdAccepted) ||
  9146. ((i == c_cmdCurrent) &&
  9147. (pship->GetPilotType() >= c_ptPlayer) &&
  9148. (pcluster = pship->GetCluster()) && //intentional =
  9149. (pship->IsUsingAreaOfEffectWeapon())))
  9150. {
  9151. BEGIN_PFM_CREATE(g.fm, pfmOC, CS, ORDER_CHANGE)
  9152. END_PFM_CREATE
  9153. pfmOC->command = i;
  9154. pfmOC->shipID = pship->GetObjectID();
  9155. pfmOC->commandID = cid;
  9156. if (ptarget)
  9157. {
  9158. pfmOC->objectType = ptarget->GetObjectType();
  9159. pfmOC->objectID = ptarget->GetObjectID();
  9160. }
  9161. else
  9162. {
  9163. pfmOC->objectType = OT_invalid;
  9164. pfmOC->objectID = NA;
  9165. }
  9166. bool fCurrentTarget = (i == c_cmdCurrent);
  9167. g.fm.SendMessages(fCurrentTarget
  9168. ? GetGroupSectorFlying(pcluster)
  9169. : CFSSide::FromIGC(pship->GetSide())->GetGroup(),
  9170. fCurrentTarget ? FM_NOT_GUARANTEED : FM_GUARANTEED, FM_FLUSH);
  9171. }
  9172. }
  9173. void FedSrvSiteBase::CreateBuildingEffect(Time now,
  9174. IasteroidIGC* pasteroid,
  9175. IshipIGC* pshipBuilder)
  9176. {
  9177. assert (pasteroid);
  9178. assert (pshipBuilder);
  9179. assert (pshipBuilder->GetPilotType() == c_ptBuilder);
  9180. assert (pshipBuilder->GetBaseData());
  9181. assert (pshipBuilder->GetCluster());
  9182. IbuildingEffectIGC* pbe = pshipBuilder->GetCluster()->CreateBuildingEffect(now,
  9183. pasteroid,
  9184. NULL,
  9185. pshipBuilder,
  9186. pasteroid->GetRadius(),
  9187. ((IstationTypeIGC*)(pshipBuilder->GetBaseData()))->GetRadius(),
  9188. pshipBuilder->GetPosition() - pshipBuilder->GetOrientation().GetBackward() * pshipBuilder->GetRadius(),
  9189. pasteroid->GetPosition());
  9190. //Export the building effect to all sides that could see the asteroid
  9191. for (SideLinkIGC* psl = m_pfsMission->GetIGCMission()->GetSides()->first();
  9192. (psl != NULL);
  9193. psl = psl->next())
  9194. {
  9195. if (pasteroid->SeenBySide(psl->data()))
  9196. {
  9197. ExportObj(pbe, OT_buildingEffect, NULL);
  9198. g.fm.SendMessages(CFSSide::FromIGC(psl->data())->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  9199. }
  9200. }
  9201. }
  9202. void FedSrvSiteBase::LayExpendable(Time now,
  9203. IexpendableTypeIGC* pet,
  9204. IshipIGC* pshipLayer)
  9205. {
  9206. assert (pet);
  9207. assert (pshipLayer);
  9208. ObjectType type = pet->GetObjectType();
  9209. ImissionIGC* pMission = m_pfsMission->GetIGCMission();
  9210. const Vector& position = pshipLayer->GetPosition();
  9211. IclusterIGC* pcluster = pshipLayer->GetCluster();
  9212. IsideIGC* pside = pshipLayer->GetSide();
  9213. if (type == OT_mineType)
  9214. {
  9215. DataMineIGC dm;
  9216. dm.pshipLauncher = NULL;
  9217. dm.psideLauncher = pside;
  9218. dm.mineID = pMission->GenerateNewMineID();
  9219. dm.time0 = now;
  9220. dm.p0 = position;
  9221. dm.pminetype = (ImineTypeIGC*)pet;
  9222. assert (dm.pminetype);
  9223. dm.pcluster = pcluster;
  9224. dm.exportF = false;
  9225. ImineIGC * m = (ImineIGC*)(pMission->CreateObject(now, OT_mine, &dm, sizeof(dm)));
  9226. assert (m);
  9227. m->Release();
  9228. }
  9229. else
  9230. {
  9231. assert (type == OT_probeType);
  9232. DataProbeIGC dp;
  9233. dp.pside = pside;
  9234. dp.pship = NULL;
  9235. dp.pmodelTarget = NULL;
  9236. dp.probeID = pMission->GenerateNewProbeID();
  9237. dp.time0 = now;
  9238. dp.p0 = position;
  9239. dp.pprobetype = (IprobeTypeIGC*)pet;
  9240. assert (dp.pprobetype);
  9241. dp.pcluster = pcluster;
  9242. dp.exportF = false;
  9243. IprobeIGC * p = (IprobeIGC*)(pMission->CreateObject(now, OT_probe, &dp, sizeof(dp)));
  9244. assert (p);
  9245. p->Release();
  9246. }
  9247. //Quietly kill the ship (after nuking its parts to prevent treasure from being created)
  9248. {
  9249. const PartListIGC* parts = pshipLayer->GetParts();
  9250. PartLinkIGC* plink;
  9251. while (plink = parts->first()) //Not ==
  9252. plink->data()->Terminate();
  9253. }
  9254. pshipLayer->SetAmmo(0);
  9255. pshipLayer->SetFuel(0.0f);
  9256. KillShipEvent(now, pshipLayer, NULL, 0.0f, position, Vector::GetZero());
  9257. }
  9258. void FedSrvSiteBase::RespawnFlag(SideID sidFlag)
  9259. {
  9260. //Don't respawn flags that have just been picked up by a ship
  9261. if (sidFlag != NA)
  9262. {
  9263. if (sidFlag == SIDE_TEAMLOBBY)
  9264. {
  9265. //Legal clusters are any neutral cluster without a flag
  9266. const int c_maxClusters = 100;
  9267. IclusterIGC* pclusters[c_maxClusters];
  9268. int nClusters = 0;
  9269. for (ClusterLinkIGC* pcl = m_pfsMission->GetIGCMission()->GetClusters()->first();
  9270. (pcl != NULL);
  9271. pcl = pcl->next())
  9272. {
  9273. IclusterIGC* pcluster = pcl->data();
  9274. if (!pcluster->GetHomeSector())
  9275. {
  9276. assert (nClusters < c_maxClusters);
  9277. pclusters[nClusters++] = pcluster;
  9278. }
  9279. }
  9280. assert (nClusters != 0);
  9281. m_pfsMission->GetIGCMission()->GenerateTreasure(g.timeNow, pclusters[randomInt(0, nClusters - 1)], -2);
  9282. }
  9283. else
  9284. {
  9285. IsideIGC* pside = m_pfsMission->GetIGCMission()->GetSide(sidFlag);
  9286. assert(pside);
  9287. //Legal clusters are any neutral cluster without a flag
  9288. const int c_maxStations = 10;
  9289. IstationIGC* pstations[c_maxStations];
  9290. int nStations = 0;
  9291. for (StationLinkIGC* psl = pside->GetStations()->first(); (psl != NULL); psl = psl->next())
  9292. {
  9293. IstationIGC* pstation = psl->data();
  9294. if (pstation->GetStationType()->HasCapability(c_sabmPedestal))
  9295. {
  9296. //Is there a flag within spitting distance?
  9297. const float c_spittingDistance = 300.0f;
  9298. TreasureLinkIGC* ptl;
  9299. for (ptl = pstation->GetCluster()->GetTreasures()->first(); (ptl != NULL); ptl = ptl->next())
  9300. {
  9301. ItreasureIGC* pt = ptl->data();
  9302. if ((pt->GetTreasureCode() == c_tcFlag) && (pt->GetTreasureID() == sidFlag))
  9303. {
  9304. float d = (pt->GetPosition() - pstation->GetPosition()).LengthSquared();
  9305. if (d < c_spittingDistance * c_spittingDistance)
  9306. break;
  9307. }
  9308. }
  9309. if (ptl == NULL)
  9310. {
  9311. assert (nStations < c_maxStations);
  9312. pstations[nStations++] = pstation;
  9313. }
  9314. }
  9315. }
  9316. assert (nStations > 0);
  9317. IstationIGC* pstation = pstations[randomInt(0, nStations - 1)];
  9318. DataTreasureIGC dt;
  9319. dt.treasureCode = c_tcFlag;
  9320. dt.treasureID = sidFlag;
  9321. dt.amount = 0;
  9322. dt.clusterID = pstation->GetCluster()->GetObjectID();
  9323. dt.lifespan = 10.0f * 24.0f * 3600.0f;
  9324. dt.createNow = false;
  9325. dt.objectID = m_pfsMission->GetIGCMission()->GenerateNewTreasureID();
  9326. dt.p0 = pstation->GetPosition();
  9327. dt.p0.z += pstation->GetRadius() + c_fFlagOffset;
  9328. dt.v0 = Vector::GetZero();
  9329. dt.time0 = g.timeNow;
  9330. ItreasureIGC* t = (ItreasureIGC *)m_pfsMission->GetIGCMission()->CreateObject(g.timeNow, OT_treasure,
  9331. &dt, sizeof(dt));
  9332. assert (t);
  9333. t->Release();
  9334. }
  9335. }
  9336. }
  9337. /*-------------------------------------------------------------------------
  9338. * FedSrvSiteBase::CreateSideEvent()
  9339. *-------------------------------------------------------------------------
  9340. * Purpose:
  9341. * When a side is created in IGC, this is called. This in turn
  9342. * creates the fsside
  9343. */
  9344. void FedSrvSiteBase::CreateSideEvent(IsideIGC * pIsideIGC)
  9345. {
  9346. m_pfsMission->CreateSide(pIsideIGC);
  9347. }
  9348. /*-------------------------------------------------------------------------
  9349. * FedSrvSiteBase::DestroySideEvent()
  9350. *-------------------------------------------------------------------------
  9351. * Purpose:
  9352. * When a side is destroyed in IGC, this is called. This
  9353. * in turn destroys the fsside.
  9354. */
  9355. void FedSrvSiteBase::DestroySideEvent(IsideIGC * pIsideIGC)
  9356. {
  9357. m_pfsMission->DeleteSide(pIsideIGC);
  9358. }
  9359. /*-------------------------------------------------------------------------
  9360. * FedSrvSiteBase::Destroy()
  9361. *-------------------------------------------------------------------------
  9362. */
  9363. void FedSrvSiteBase::Destroy(CFSMission * pfsMission)
  9364. {
  9365. delete this;
  9366. }
  9367. IshipIGC* PickNewLeader(const ShipListIGC* pships,
  9368. IshipIGC* pshipIgnore,
  9369. int maxVotes)
  9370. {
  9371. struct Contender
  9372. {
  9373. IshipIGC* pship;
  9374. int nVotes;
  9375. };
  9376. Contender* pcontenders = new Contender[pships->n()];
  9377. int nContenders = 0;
  9378. IshipIGC* pshipMax = NULL;
  9379. for (ShipLinkIGC* psl = pships->first(); (psl != NULL); psl = psl->next())
  9380. {
  9381. IshipIGC* pshipAD = psl->data()->GetAutoDonate();
  9382. if (pshipAD && (pshipAD != pshipIgnore))
  9383. {
  9384. int i;
  9385. for (i = 0; (i < nContenders); i++)
  9386. {
  9387. if (pcontenders[i].pship == pshipAD)
  9388. {
  9389. pcontenders[i].nVotes++;
  9390. break;
  9391. }
  9392. }
  9393. if (i == nContenders)
  9394. {
  9395. pcontenders[nContenders].pship = pshipAD;
  9396. pcontenders[nContenders++].nVotes = 1;
  9397. }
  9398. if (pcontenders[i].nVotes > maxVotes)
  9399. {
  9400. maxVotes = pcontenders[i].nVotes;
  9401. pshipMax = pcontenders[i].pship;
  9402. }
  9403. }
  9404. }
  9405. delete pcontenders;
  9406. return pshipMax;
  9407. }
  9408. /*-------------------------------------------------------------------------
  9409. * FedSrvApp
  9410. *-------------------------------------------------------------------------
  9411. * Purpose:
  9412. * This class is called by the assert code when an assert happens
  9413. */
  9414. #ifdef _DEBUG
  9415. void FlushDebugLog(void)
  9416. {
  9417. g_app.CloseLogFile();
  9418. }
  9419. VOID CALLBACK FileIOCompletionRoutine(
  9420. DWORD dwErrorCode, // completion code
  9421. DWORD dwNumberOfBytesTransfered, // number of bytes transferred
  9422. LPOVERLAPPED lpOverlapped // I/O information buffer
  9423. )
  9424. {
  9425. delete [] (char*)lpOverlapped;
  9426. }
  9427. void FedSrvApp::AsyncFileOut(const char *psz)
  9428. {
  9429. SYSTEMTIME systime;
  9430. DWORD l = strlen(psz);
  9431. const c_cbPrefix = 19; // time format: 12/31 23:59:59.999
  9432. DWORD cbLine = l + c_cbPrefix;
  9433. OVERLAPPED* pov = (OVERLAPPED*)(new char [ sizeof(OVERLAPPED) + cbLine ]);
  9434. *pov = m_overlapped;
  9435. GetLocalTime(&systime);
  9436. wsprintf((char*)(pov + 1), "%02d/%02d %02d:%02d:%02d.%03d ", systime.wMonth, systime.wDay,
  9437. systime.wHour, systime.wMinute, systime.wSecond, systime.wMilliseconds);
  9438. //_strtime((char*)(pov + 1));
  9439. //((char*)(pov + 1))[8] = ' ';
  9440. memcpy(((char*)(pov + 1)) + c_cbPrefix, psz, l);
  9441. pov->Offset = ::InterlockedExchangeAdd(&m_nOffset, cbLine);
  9442. WriteFileEx(m_hFile, pov + 1, cbLine, pov, FileIOCompletionRoutine);
  9443. }
  9444. void FedSrvApp::DebugOutput(const char *psz)
  9445. {
  9446. static TCAutoCriticalSection critsec;
  9447. Time tStart = Time::Now();
  9448. critsec.Lock();
  9449. if (m_dwDebug & FED_DEBUG_FILE)
  9450. AsyncFileOut(psz);
  9451. if (m_dwDebug & FED_DEBUG_DEBUGOUT)
  9452. ::OutputDebugStringA(psz);
  9453. Time tStop = Time::Now();
  9454. DWORD dt = (tStop.clock() - tStart.clock());
  9455. if (dt > 20)
  9456. {
  9457. static char bfr[200];
  9458. sprintf(bfr, "DebugOutput delay of %f: %d %d\n", float(dt) / 1000.0f, tStart.clock(), tStop.clock());
  9459. if (m_dwDebug & FED_DEBUG_FILE)
  9460. AsyncFileOut(bfr);
  9461. if (m_dwDebug & FED_DEBUG_DEBUGOUT)
  9462. ::OutputDebugStringA(bfr);
  9463. }
  9464. critsec.Unlock();
  9465. }
  9466. bool FedSrvApp::OnAssert(const char* psz, const char* pszFile, int line, const char* pszModule)
  9467. {
  9468. _AGCModule.TriggerEvent(NULL, EventID_AGCAssert, "", -1, -1, -1, 3,
  9469. "File", VT_LPSTR, pszFile,
  9470. "Line", VT_I4, line,
  9471. "Text", VT_LPSTR, psz);
  9472. return g.fWantInt3;
  9473. }
  9474. void FedSrvApp::OnAssertBreak()
  9475. {
  9476. if (m_hFile)
  9477. {
  9478. CloseHandle(m_hFile);
  9479. m_hFile = NULL;
  9480. }
  9481. *(DWORD*)0 = 0;
  9482. //DebugBreak();
  9483. }
  9484. // Global Initialization
  9485. FedSrvApp g_app;
  9486. #endif