001 #ifndef FUN_WITH_FRIENDS
002 #define FUN_WITH_FRIENDS "poop"
003 #include "Set.HC"
004 #define TOOM_PORT 6666 //FreeBSD doesnt like low ports
005 class CServerSidedef {
006   I64 idx;
007   U8 upper_texture[16];
008   U8 middle_texture[16];
009   U8 lower_texture[16];
010   Bool changed;
011 };
012 
013 class CSidedefsHeader {
014   U64 ident;
015   U64 sz;
016   CServerSidedef body[0];
017 };
018 
019 
020 
021 class CServerSector {
022   I32 idx;
023   I16 floor_height,ceil_height;
024   U8 floor_texture[16];
025   U8 ceil_texture[16];
026   Bool changed;
027 };
028 
029 class CSectorsHeader {
030   U64 ident;
031   U64 sz;
032   CServerSector body[0];
033 };
034 
035 class CMPConnect:CQue {
036   I64 sock,player_num;
037   CDoomThing *player;
038   CServerSidedef *sidedefs;
039   CServerSector *sectors;
040 };
041 
042 
043 U16 MPHashThing(CDoomThing *t) {
044   if(!t->template) return 0;
045 //All cordnates will be rounded to 8 to avoid sending lots of data,I doubt a player would notice
046   U8 buf[STR_LEN],*tmp;
047   I64 hash=(t->height>>3*67+t->y>>3*5+t->x>>3);
048   if(tmp=GetUNStr(t->animation))
049     hash^=HashStr(tmp);
050   return (hash&0xffff)|1;
051 }
052 #define MSG_HASH_THINGS 'HashTngs'
053 #define MSG_WANT_HASH_THINGS 'WantTHsh'
054 #define MSG_WANT_THINGS 'WantTngs'
055 #define MSG_SEND_THINGS 'SendTngs'
056 #define MSG_SEND_PLAYER 'CSndPlr' //Client send player
057 #define MSG_PLAYER_ATTACK 'PlrAtk'
058 #define MSG_PLAYER_MISSILE 'PlrMsl'
059 #define MSG_PLAYER_UPDATE 'PlrUpdt'
060 //From the client only,picked up items from server are removed via MSG_SEND_THINGS
061 // For removing items see:
062 //   RecieveThings and CompareThingHashes
063 #define MSG_ITEM_PICKUP 'ItmPkup'
064 //Sent by Client,no body
065 #define MSG_SERVER_WHAT_LEVEL 'Wassup'
066 //Sent by Server,also uses `CWhatLevelHeader`
067 #define MSG_SERVER_REPLY_LEVEL 'thslvl'
068 //Sent by server x client
069 #define MSG_PLAY_SOUND 'PlySnd'
070 
071 #define MSG_WANT_WORLD_THINKER_HASHES 'WrldHshW'
072 #define MSG_WORLD_SECTOR_HASHES 'SctrHshs'
073 #define MSG_WORLD_SEND_SECTORS 'SndSctrs'
074 
075 //See CMPConnect.last_known_linedefs/sectors
076 #define MSG_WORLD_SIDEDEF_HASHES 'SdHshs'
077 #define MSG_WORLD_SEND_SIDEDEFS 'SndSds'
078 
079 #define MSG_PRESS_A_WALL 'PrsWll'
080 #define MSG_TRIGGER_LINEDEF 'TrgrLd'
081 
082 class CServerThing {
083   F64 anim_start_tS;
084   I16 x,y,h;
085   I16 angle;
086   I16 health;
087   U8 template[24];
088   U8 anim_name[24];
089 };
090 class CServerPlayer:CServerThing {
091   F64 rad_suit_time;
092   F64 bezerk_time;
093   F64 light_goggles_time;
094   F64 invincible_time;
095   F64 invisible_time;
096   I64 additional_weapon_ammo[8];
097   U8 has_weapons[8];
098   Bool made_a_sound;
099 };
100 class CGenericHeader {
101   U64 ident;
102   I64 sz;
103 };
104 class CWhatLevelHeader {
105   U64 ident;
106   I64 sz;
107   U0 body;
108   U8 map_name[16];
109   I64 skill,which_spawn;
110 }
111 class CPickupHeader {
112   U64 ident;
113   I64 sz;
114   U0 body;
115   I64 thing_hash;
116 };
117 class CSoundHeader {
118   U64 ident;
119   I64 sz;
120   U0 body;
121   I16 x,y;
122   U8 name[9];
123 };
124 class CThingsHeader {
125   U64 ident;
126   I64 sz;
127   CServerThing body[0];
128 };
129 class CHashesHeader {
130   U64 ident;
131   I64 sz;
132   U16 body[0];
133 };
134 class CAskforHeader {
135   U64 ident;
136   I64 sz;
137   U64 what;
138 };
139 class CLinedefSend {
140   U64 ident;
141   I64 sz;
142   U0 body;
143   I64 ld_idx;
144   I64 flags;
145 };
146 class CMissileSend {
147   U64 ident;
148   I64 sz;
149   U0 body;
150   U64 type;
151   I64 target_hash;
152   I64 sx,sy;
153   I64 ex,ey;
154 };
155 class CPlayerAttack {
156   U64 ident;
157   I64 sz;
158   U0 body;
159   I16 target_hash;
160   I16 damage;
161   I64 fallback_angle;
162 };
163 
164 class CWallPress {
165   U64 ident;
166   I64 sz;
167   U0 body;
168   I16 angle;
169   I16 x,y,height;
170 };
171 
172 extern U0 MPClientSync(CMPConnect *con,CDoomLevel *l);
173 
174 U0 SendMessage(CMPConnect *con,CGenericHeader *msg) {
175 PrintI(&msg->ident,msg->sz);
176   CArcCompress *buf=CompressBuf(msg,msg->sz);
177   NetWrite(con->sock,buf,buf->compressed_size);
178   Free(buf);
179 }
180 CGenericHeader *GetMessage(CMPConnect *con) {
181 PrintI("ask",0);
182   CArcCompress head,*b1;
183   CGenericHeader *b2;
184   NetRead(con->sock,&head,sizeof (CArcCompress));
185   b1=CAlloc(head.compressed_size+sizeof(CArcCompress));
186   MemCpy(b1,&head,sizeof CArcCompress);
187   NetRead(con->sock,b1+1,head.compressed_size-sizeof(CArcCompress));
188   b2=ExpandBuf(b1);
189   Free(b1);
190 PrintI(&b2->ident,b2->sz);
191   return b2;
192 }
193 
194 Bool Server_MPConnectUpdateSidedefs(CMPConnect *con,CDoomLevel *l) {
195   CDoomSidedef *sd,*head=&l->sidedefs; 
196   CServerSidedef *srv_sd;
197   CSidedefsHeader *msg;
198   Bool changed=FALSE;
199   U64 dummy;
200   I64 cnt=0,change_cnt=0;
201   if(!con->sidedefs)
202     con->sidedefs=CAlloc(sizeof(CServerSidedef)*QueCnt(head));
203   for(sd=head->next;sd!=head;sd=sd->next) {
204     srv_sd=&con->sidedefs[cnt];
205     srv_sd->idx=idx;
206     srv_sd->changed=FALSE;
207     if(FramePtr(srv_sd->upper_texture)!=sd->upper_texture[0]&&sd->upper_texture[0]) {
208       changed=TRUE;
209       srv_sd->changed=TRUE;
210       StrCpy(srv_sd->upper_texture,GetUNStr(sd->upper_texture[0]));
211     }
212 
213     if(FramePtr(srv_sd->lower_texture)!=sd->lower_texture[0]&&sd->lower_texture[0]) {
214       changed=TRUE;
215       srv_sd->changed=TRUE;
216       StrCpy(srv_sd->lower_texture,GetUNStr(sd->lower_texture[0]));
217     }
218 
219     if(FramePtr(srv_sd->middle_texture)!=sd->middle_texture[0]&&sd->middle_texture[0]) {
220       changed=TRUE;
221       srv_sd->changed=TRUE;
222       StrCpy(srv_sd->middle_texture,GetUNStr(sd->middle_texture[0]));
223     }
224 
225     if(srv_sd->changed)
226       change_cnt++;
227     cnt++;
228   }
229 
230   if(change_cnt) {
231     msg=CAlloc(sizeof(CServerSidedef)*change_cnt+sizeof(CSidedefsHeader));
232     msg->ident=MSG_WORLD_SEND_SIDEDEFS;
233     msg->sz=sizeof(CServerSidedef)*change_cnt+sizeof(CSidedefsHeader);
234     cnt=0;
235     change_cnt=0;
236     for(sd=head->next;sd!=head;sd=sd->next) {
237       srv_sd=&con->sidedefs[cnt];
238       if(srv_sd->changed)
239         MemCpy(&msg->body[change_cnt++],srv_sd,sizeof(CServerSidedef));
240       cnt++;
241     }
242     SendMessage(con,msg);
243     Free(msg);
244   }
245 
246   return changed;
247 }
248 
249 Bool Server_MPConnectUpdateSectors(CMPConnect *con,CDoomLevel *l) {
250   CDoomSector *sec,*head=&l->sectors; 
251   CServerSector *srv_sec;
252   CSectorsHeader *msg;
253   I64 cnt=0,change_cnt=0;
254   Bool changed=FALSE;
255   U64 dummy;
256   if(!con->sidedefs)
257     con->sidedefs=CAlloc(sizeof(CServerSector)*QueCnt(head));
258   for(sec=head->next;sec!=head;sec=sec->next) {
259     srv_sec=&con->sectors[cnt];
260     srv_sec->idx=idx;
261     srv_sec->changed=FALSE;
262 
263     if(FramePtr(srv_sec->ceil_texture)!=sec->ceil_dc[0]) {
264       srv_sec->changed=TRUE;
265       changed=TRUE;
266       StrCpy(srv_sec->ceil_texture,GetUNStr(sec->ceil_dc[0]));
267     }
268 
269     if(FramePtr(srv_sec->floor_texture)!=sec->floor_dc[0]) {
270       srv_sec->changed=TRUE;
271       changed=TRUE;
272       StrCpy(srv_sec->ceil_texture,GetUNStr(sec->ceil_dc[0]));
273     }
274 
275     if(srv_sec->floor_height!=ToI64(sec->floor_height)) {
276       srv_sec->floor_height=ToI64(sec->floor_height);
277       changed=TRUE;
278       srv_sec->changed=TRUE;
279     }
280     if(srv_sec->ceil_height!=ToI64(sec->ceil_height)) {
281       srv_sec->ceil_height=ToI64(sec->ceil_height);
282       changed=TRUE;
283       srv_sec->changed=TRUE;
284     }
285 
286     if(srv_sec->changed)
287       change_cnt++;
288     cnt++;
289   }
290 
291   if(change_cnt) {
292     msg=CAlloc(sizeof(CServerSector)*change_cnt+sizeof(CSectorsHeader));
293     msg->ident=MSG_WORLD_SEND_SECTORS;
294     msg->sz=sizeof(CServerSector)*change_cnt+sizeof(CSectorsHeader);
295     DbgPrint("sendin out %d sectors\n",change_cnt);
296     cnt=0;
297     change_cnt=0;
298     for(sec=head->next;sec!=head;sec=sec->next) {
299       srv_sec=&con->sidedefs[cnt];
300       if(srv_sec->changed) {
301         MemCpy(&msg->body[change_cnt++],srv_sec,sizeof(CServerSector));
302       }
303       cnt++;
304     }
305     SendMessage(con,msg);
306     Free(msg);
307   }
308   return changed;
309 }
310 
311 
312 
313 U0 SendThingHashes(CMPConnect *con,CDoomLevel *l) {
314   I64 cnt=QueCnt(&l->things),idx,h;
315   if(con->player) cnt--;
316   CDoomThing *t;
317   CHashesHeader *msg=CAlloc(cnt*sizeof(I16)+sizeof(CHashesHeader));
318   msg->ident=MSG_HASH_THINGS;
319   msg->sz=cnt*2+sizeof(CHashesHeader);
320   t=l->things.next;
321   for(idx=0;idx!=cnt;idx++) {
322     h=MPHashThing(t);
323     if(t!=con->player)
324       msg->body[idx]=h;
325     t=t->next;
326   }
327   SendMessage(con,msg);
328   Free(msg);
329 }
330 
331 //Assumes CHashesHeader was read already
332 U0 CompareThingHashes(CMPConnect *con,CDoomLevel *l,CHashesHeader *hd) {
333   I64 me_cnt=QueCnt(&l->things),you_cnt=hd->sz/8;
334   CDoomThing *t,*t2;
335   I64 idx,h;
336   CI64Set *want=I64SetNew,*to_keep=I64SetNew;
337   CI64Set *collisions=I64SetNew;
338   U16 *you_hashes;
339   CHashesHeader *hashes_msg;
340   t=l->things.next;
341   for(idx=0;idx!=me_cnt;idx++) {
342     h=MPHashThing(t);
343     t->hash=h;
344     t=t->next;
345     
346   }
347   you_hashes=hd->body;
348 //Pass 1,check for changes
349   for(idx=0;idx!=hd->sz/2;idx++) {
350     h=you_hashes[idx];
351     if(!I64SetAdd(want,h))
352       I64SetAdd(collisions,h);
353   }
354 //Pass 2,remove items that dont appear in you_hashes
355   for(idx=0;idx!=hd->sz/2;idx++) {
356     h=you_hashes[idx];
357     if(I64SetHas(want,h)&&!I64SetHas(collisions,h))
358       I64SetAdd(to_keep,h);
359   }
360   t=l->things.next;
361   for(idx=0;idx!=me_cnt;idx++) {
362     t2=t->next;
363     if(!I64SetHas(to_keep,MPHashThing(t))) {
364       if(t->thinker) {
365         QueRem(t->thinker);
366         Free(t->thinker);
367       }
368       ThingDel(l,t);
369     }
370     t=t2;
371   }
372 
373   PrintI("ch",2);
374 
375   hashes_msg=CAlloc(want->cnt*2+sizeof(CHashesHeader));
376   hashes_msg->ident=MSG_WANT_THINGS;
377   hashes_msg->sz=want->cnt*2;
378   for(idx=0;idx!=want->cnt;idx++)
379     hashes_msg->body[idx]=want->body[idx];
380   SendMessage(con,hashes_msg);
381   Free(hashes_msg);
382   I64SetDel(want);
383   I64SetDel(collisions);
384   I64SetDel(to_keep);
385 }
386 
387 U0 RecieveThings(CMPConnect *con,CDoomLevel *l,CThingsHeader *got) {
388   I64 cnt=got->sz/sizeof(CServerThing);
389   I64 idx;  
390   CDoomThing *dt;
391   CServerThing *st=got->body;
392   for(idx=0;idx!=cnt;idx++) {
393     dt=CAlloc(sizeof(CDoomThing),doom_task);
394     QueIns(dt,l->things.last);
395     dt->x=st->x;
396     dt->y=st->y;
397     dt->height=st->h;
398     dt->angle=Wrap(ToF64(st->angle))/pi*I16_MAX;
399     dt->anim_start_tS=st->anim_start_tS;
400     dt->template=FramePtr(st->template);
401     dt->animation=FramePtr(st->anim_name);
402     st++;
403   }
404 }
405 U0 SendThings(CMPConnect *con,CDoomLevel *l,CHashesHeader *msg) {
406   I64 cnt;
407   I64 idx,dummy;  
408   CServerThing thing;
409   CDoomThing *dt;
410   CI64Set *want=I64SetNew;
411   cnt=(msg->sz-sizeof(CHashesHeader))/sizeof(I16);
412   dummy=0;
413   while(--cnt>=0) {
414     I64SetAdd(want,msg->body[cnt]);
415   }
416   dt=l->things.next;
417   cnt=0;
418   for(;dt!=&l->things;dt=dt->next) {
419     if(dt!=con->player&&I64SetHas(dt,MPHashThing(dt)))
420       cnt++;
421   }
422   dt=l->things.next;
423   msg=CAlloc(sizeof(CThingsHeader)+sizeof(CServerThing)*cnt);
424   msg->ident=MSG_SEND_THINGS;
425   msg->sz=sizeof(CServerThing)*cnt+sizeof(CThingsHeader);
426   for(dt;dt!=&l->things;dt=dt->next) {
427     if(dt!=con->player&&I64SetHas(want,MPHashThing(dt))) {
428       thing.x=dt->x;
429       thing.y=dt->y;
430       thing.h=dt->height;
431       StrCpy(thing.template,GetUNStr(dt->template));
432       StrCpy(thing.anim_name,GetUNStr(dt->animation));
433     }
434   }
435   SendMessage(con,msg);
436   I64SetDel(want);
437 }
438 
439 U0 SendPlayerSpawnMissile(CMPConnect *con,CDoomPlayer *p,CD2 *to,CDoomThing *target,U64 type) {
440   MPClientSync(con,l);
441   CMissileSend mss;
442   mss.ident=MSG_PLAYER_MISSILE;
443   mss.sz=sizeof(CMissileSend);
444   mss.sx=p->x;
445   mss.sy=p->y;
446   mss.ex=to->x;
447   mss.ey=to->y;
448   mss.type=type;
449   if(target) mss.target_hash=MPHashThing(target);
450   else mss.target_hash=0;
451   SendMessage(con,&mss);
452 }
453 U0 SendPlayerAttack(CMPConnect *con,CDoomLevel *l,CDoomPlayer *p,CDoomThing *tar,F64 damage) {
454   MPClientSync(con,l);
455   CPlayerAttack at;
456   F64 angle;
457   if(tar)
458     angle=FArg(tar->x-p->x,tar->y-p->y);
459   else
460     angle=p->angle;
461   at.ident=MSG_PLAYER_ATTACK;
462   at.sz=sizeof(CPlayerAttack)-offset(CMissileSend.body);
463   if(tar)
464     at.target_hash=MPHashThing(tar);
465   else
466     at.target_hash=0;
467   at.damage=damage;
468   at.fallback_angle=Wrap(angle)/pi*I16_MAX;
469   SendMessage(con,&at);
470 }
471 U0 ReadPlayerUpdateClient(CMPConnect *con,CThingsHeader *hh) {
472   CDoomPlayer *p=con->player;
473   CServerPlayer *thg=hh->body;
474   I64 idx;
475 //Cleint's handle the position
476 /*  p->x=thg.x;
477   p->y=thg.y;
478   p->h=thg.h;  
479   p->angle=thg.angle/ToF64(I16_MAX)*pi;*/
480 
481 //Maybe an enemy in the server attacks the player
482   p->health=thg->health;
483   for(idx=0;idx!=8;idx++) {
484     p->arsenal[idx].available=thg->has_weapons[idx];
485 //These are aliased (3->1 and 7->6)
486     if(idx!=3&&idx!=7)
487       p->arsenal[idx].load->ammo+=thg->additional_weapon_ammo[idx];
488   }
489   p->rad_suit_time=thg->rad_suit_time;
490   p->bezerk_time=thg->bezerk_time;
491   p->light_goggles_time=thg->light_goggles_time;
492   p->invincible_time=thg->invincible_time;
493   p->invisible_time=thg->invisible_time;
494 }
495 
496 U0 SendPickup(CMPConnect *con,CDoomThing *thing) {
497   MPClientSync(con,l);
498   CPickupHeader puh;
499   puh.ident=MSG_ITEM_PICKUP;
500   puh.sz=sizeof(CPickupHeader);
501   puh.thing_hash=MPHashThing(thing);
502   SendMessage(con,&puh);
503 }
504 
505 U0 ReadPlayerUpdateServer(CMPConnect *con,CThingsHeader *hh) {
506   CDoomPlayer *p=con->player;
507   CServerPlayer *thg=hh->body;
508   I64 idx;
509 //Cleint's handle the position
510   DbgPrint("Server gxt player(%d,%d)\n",thg->x,thg->y); 
511   p->x=thg->x;
512   p->y=thg->y;
513   p->height=thg->h;  
514   p->angle=thg->angle/ToF64(I16_MAX)*pi;
515 
516 //Maybe an enemy in the server attacks the player
517 //Server handles health
518 //Clients handle ammo,server only adds ammo to players
519 }
520 
521 
522 U0 SendPlayerUpdate(CMPConnect *con,CDoomPlayer *p) {
523   CThingsHeader *hdr=CAlloc(sizeof(CThingsHeader)+sizeof(CServerPlayer));
524   CServerPlayer *sp;
525   I64 idx;
526   hdr->ident=MSG_SEND_PLAYER;
527   hdr->sz=sizeof(CServerThing);
528   sp=&hdr->body;
529   sp->x=p->x;
530   sp->y=p->y;
531   sp->h=p->height;
532   sp->health=p->health;
533   sp->angle=Wrap(p->angle)/pi*I16_MAX;
534   hdr->sz=sizeof(CServerPlayer);
535   DbgPrint("cleint sends plxyer\n");
536   SendMessage(con,hdr);
537   Free(hdr);
538 }
539 U0 MPClientSync(CMPConnect *con,CDoomLevel *l) {
540   CHashesHeader want,*hh;
541   want.ident=MSG_WANT_HASH_THINGS;
542   want.sz=sizeof(CGenericHeader);
543   SendMessage(con,&want);
544   hh=GetMessage(con);
545   if(hh->ident!=MSG_HASH_THINGS)
546     throw('Server');
547   CompareThingHashes(con,l,hh);
548   Free(hh);
549   want.ident=MSG_WANT_HASH_THINGS;
550   want.sz=sizeof(CGenericHeader);
551   SendMessage(con,&want);
552   hh=GetMessage(con);
553   if(hh->ident!=MSG_SEND_THINGS)
554     throw('Server');
555   RecieveThings(con,l,hh);
556   Free(hh);
557 PrintI("mps",3);
558 }
559 U0 ServerExit() {
560   CMPConnect *head=FramePtr("Srv.Connects"),*cur,*next;
561   for(cur=head->next;cur!=head;cur=next) {
562     next=cur->next;
563     NetClose(cur->sock);
564     Free(cur);
565   }
566   Free(head);
567   NetClose(FramePtr("Srv.Socket"));
568   Exit;
569 }
570 CMPConnect *MPCloseConnect(CMPConnect *con) {
571   CMPConnect *next=con->next;
572   CDoomPlayer *p;
573   if(p=con->player) {
574     p->anim_start_tS=Server_tS;
575     p->anim_no_repeat=TRUE;
576     p->animation=p->template(CDoomMonsterTemplate*)->_gib_frames;
577     p->health=0;
578     p->flags&=~TF_OBSTACLE;
579   }
580   NetClose(con->sock);
581   QueRem(con);
582   Free(con);
583   return next;
584 }
585 U0 ServerBegin() {
586   CNetAddr *addr=NetAddrNew("0.0.0.0",TOOM_PORT);
587   I64 listen_sock=NetSocketNew,idx,new_sock;
588   CMPConnect *connects=CAlloc(sizeof CQue);
589   QueInit(connects);
590   FramePtrAdd("Srv.Connects",connects);
591   FramePtrAdd("Srv.Socket",listen_sock);
592   NetBindIn(listen_sock,addr);
593   NetListen(listen_sock,4); //4 players
594   NetAddrDel(addr);
595 }
596 U0 ServerUpdate() {
597   I64 listen_sock=FramePtr("Srv.Socket"),idx,new_sock;
598   Bool avail[4];
599   CMPConnect *connects=FramePtr("Srv.Connects"),*new,*next_con;
600   F64 angle;
601   CDoomPlayer *p;
602   CDoomThing *dthing;
603   CGenericHeader *msg;
604   U8 *buf;
605   while(TRUE) {
606     if(-1!=NetPollForRead(1,&listen_sock)) {
607 DbgPrint("nw client:\n");
608       new_sock=NetAccept(listen_sock,NULL);
609       new=connects->next;
610       MemSet(avail,TRUE,4);
611       for(idx=QueCnt(connects)-1;idx>=0;idx--) {
612         avail[new->player_num]=FALSE;
613         new=new->next;
614       }
615       for(idx=0;idx!=4;idx++) {
616         if(avail[idx]) {
617 PrintI("avail",idx);
618           p=PlayerNew(l);
619           new=CAlloc(sizeof CMPConnect);
620           new->sock=new_sock;
621           QueIns(new,connects);
622           new->player=p;
623           goto pass;
624         }
625       }
626 //TODO game full Or some shit
627       NetClose(new_sock);
628       goto pass;
629     }
630     for(new=connects->next;new!=connects;new=next_con) {
631       next_con=new->next;
632       if(-1!=NetPollForRead(1,&new->sock)) {
633         msg=GetMessage(new);
634         DbgPrint("request %c\n",msg->ident);
635         if(msg->ident==MSG_HASH_THINGS) {
636 wtf:
637           DbgPrint("WTF\n");
638         } else if(msg->ident==MSG_WANT_HASH_THINGS) {
639           DbgPrint("Client asked for hahses\n");
640           SendThingHashes(new,l);
641         } else if(msg->ident==MSG_WANT_THINGS) {
642           DbgPrint("Client asked for things\n");
643           SendThings(new,l,msg);
644         } else if(msg->ident==MSG_SEND_THINGS) {
645           goto wtf;
646         } else if(msg->ident==MSG_TRIGGER_LINEDEF) {
647           CLinedefSend *ld_send;
648           DbgPrint("Client triggers linedef(%d) with flags %X\n",ld_send->ld_idx,ld_send->flags);
649           TriggerLinedef(l,GetNthLinedef(l,ld_send->ld_idx),ld_send->flags,new->player);
650         } else if(msg->ident==MSG_PLAYER_ATTACK) {
651           CPlayerAttack *patk=msg;
652           DbgPrint("Player begins attack\n");
653           for(dthing=l->things.next;dthing!=&l->things;dthing=dthing->next) {
654             if(patk->target_hash==MPHashThing(dthing)) {
655               DbgPrint("Player attack target found\n");
656               DamageMob(l,new->player,dthing,patk->damage);
657               goto after_player_attack;
658             }
659           }
660 //Fallback
661           DbgPrint("Player attack target not found,fallback angle(%n)\n",patk->fallback_angle/ToF64(I16_MAX)*180.);
662           LineAttack(l,new->player,NULL,
663                 64*128.,
664                 patk->fallback_angle/ToF64(I16_MAX)*pi,
665                 patk->damage);
666 after_player_attack:;
667         } else if(msg->ident==MSG_PLAYER_MISSILE) {
668           CMissileSend *missile_send=msg;
669           DbgPrint("Player spawns missile(type %c)\n",missile_send->type);
670           for(dthing=l->things.next;dthing!=&l->things;dthing=dthing->next) {
671             if(patk->target_hash==MPHashThing(dthing)) {
672               DbgPrint("Player finds target to fire missile at\n");
673               SpawnMissile(l,new->player,dthing,missile_send->type);
674               goto after_missile_attack;
675             }
676           }
677           DbgPrint("Player blind fires missoile(no matched target at %n)\n",
678                 180/pi*FArg(
679                 missile_send->ex-missile_send->sx,
680                 missile_send->ey-missile_send->sy
681                 )
682           );
683           angle=new->player->angle;
684           new->player->angle=FArg(
685                 missile_send->ex-missile_send->sx,
686                 missile_send->ey-missile_send->sy
687                 );
688           SpawnMissile(l,new->player,NULL,missile_send->type);
689           new->player->angle=angle;
690 after_missile_attack:;
691         } else if(msg->ident==MSG_SEND_PLAYER) {
692           DbgPrint("Player updtes position.\n");
693           ReadPlayerUpdateServer(new,msg);
694         } else if(msg->ident==MSG_ITEM_PICKUP) {
695           CPickupHeader *puh=msg;
696           DbgPrint("Player attempts to pickup item\n");
697           for(dthing=l->things.next;dthing!=&l->things;dthing=dthing->next) {
698             if(MPHashThing(dthing)==puh->thing_hash&&dthing->flags&(TF_ARTIFACT|TF_POWERUP)) {
699               DbgPrint("Player pickup item hash matched\n");
700               ThingDel(l,dthing);
701               break;
702             }
703           }
704         } else if(msg->ident==MSG_PLAY_SOUND) {
705           CSoundHeader *shdr=msg;
706           DbgPrint("Sound %s is at %d,%d\n",shdr->name,shdr->x,shdr->y);
707           for(next_con=connects->next;next_con!=connects;next_con=next_con->next)
708             if(next_con!=new)
709               SendMessage(next_con,shdr);
710         } else if(msg->ident==MSG_SERVER_WHAT_LEVEL) {
711           DbgPrint("Cleint asks what level\n");
712           CWhatLevelHeader what_lvl;
713           what_lvl.ident=MSG_SERVER_WHAT_LEVEL;
714           what_lvl.sz=sizeof(CWhatLevelHeader);
715           StrCpy(what_lvl.map_name,l->map_name);
716           what_lvl.skill=l->skill;
717           SendMessage(new,&what_lvl);
718         } else {
719 //Fuck TODO give client a decription of error
720           //close connection
721           DbgPrint("Ohg fuck closing connection(got %c)\n",msg->ident);
722           MPCloseConnect(new);
723         }
724         Free(msg);
725         goto pass;
726       } else if(-1!=NetPollForHangup(1,&new->sock)) {
727         DbgPrint("hup closing connenction\n");
728         MPCloseConnect(new);
729       }
730     }
731     break;
732 pass:;
733   }
734   for(next_con=connects;next_con!=connects;next_con=next_con->next) {
735     Server_MPConnectUpdateSidedefs(next_con,l);
736     Server_MPConnectUpdateSectors(next_con,l);
737   }
738 }
739 Bool IsNetworkGameClient() {
740   if(FramePtr("Client.Connect"))
741     return TRUE;
742   return FALSE;
743 }
744 
745 U0 ClientDisconnect() {
746   Free(FramePtr("Client.Connect"));
747   NetClose(FramePtr("Client.Sock"));
748   Exit;
749 }
750 Bool ClientBegin(U8 *who) {
751   I64 listen_sock=NetSocketNew;
752   Bool success=TRUE;
753   CNetAddr *addr=NetAddrNew(who,TOOM_PORT);
754   CWhatLevelHeader what_lvl,*msg;
755   CDoomLevel *oldl=l;
756   CMPConnect *connect=CAlloc(sizeof(CMPConnect));
757   connect->sock=listen_sock;
758   Fs->task_end_cb=&ClientDisconnect;
759   NetConnect(listen_sock,addr);
760   FramePtrAdd("Client.Sock",listen_sock);
761   FramePtrAdd("Client.Connect",connect);
762   NetAddrDel(addr);
763   what_lvl.ident=MSG_SERVER_WHAT_LEVEL;
764   what_lvl.sz=sizeof CGenericHeader;
765   SendMessage(connect,&what_lvl);
766   while(TRUE) {
767     if(-1!=NetPollForRead(1,&listen_sock)) {
768       msg=GetMessage(connect);
769 DbgPrint("G:%c\n",what_lvl.ident);
770       if(msg->ident==MSG_SERVER_WHAT_LEVEL) {
771 //TODO load level multiplayer
772         if(l=LoadWadLevel(msg->map_name,msg->skill)) {
773           DbgPrint("Server is on map %s skill %d\n",msg->map_name,msg->skill);
774           if(oldl) FreeLevel(oldl);
775           QueDel(&l->things);
776           QueInit(&l->things);
777 //TODO server level transitions
778           p=PlayerNew(l);
779           connect->player=p;
780           break;
781         } else {
782 //TODO inform client something bad happended
783           NetClose(connect->sock);
784           Free(connect);
785           success=FALSE;
786           break;
787         }
788         
789       } else 
790         break;
791     } else
792       Sleep(2);
793   }
794   return success;
795 }
796 U0 ClientUpdate(CDoomLevel *l) {
797   I64 listen_sock=FramePtr("Client.Sock");
798   CMPConnect *connect=FramePtr("Client.Connect");
799   CGenericHeader *msg;
800   I64 cnt;
801   CServerSidedef *srv_sd;
802   CServerSector *srv_sec;
803   CDoomSidedef *sd;
804   CDoomSector *sec;
805   while(TRUE) {
806     if(-1!=NetPollForRead(1,&listen_sock)) {
807       msg=GetMessage(connect);
808       if(msg->ident==MSG_PLAY_SOUND) {
809         CSoundHeader *snd=msg;
810         DbgPrint("Cleint gxt a sound(%s)\n",snd->name);
811         Spawn(&PlaySoundTask,snd->name,"snd",0,Fs);
812       } else if(msg->ident==MSG_WORLD_SEND_SIDEDEFS) {
813         CSidedefsHeader *sidedefs=msg;
814         cnt=sidedefs->sz/sizeof(CServerSidedef);
815         DbgPrint("Cleint gxt %d sidedefs\n",cnt);
816         while(--cnt>=0) {
817           srv_sd=&sidedefs->body[cnt];
818           if(srv_sd->idx!=-1) {
819             sd=GetNthSidedef(l,srv_sd->idx);
820             sd->upper_texture[0]=FramePtr(srv_sd->upper_texture);
821             sd->middle_texture[0]=FramePtr(srv_sd->middle_texture);
822             sd->lower_texture[0]=FramePtr(srv_sd->lower_texture);
823             DbgPrint("sd(%d)%s,%s,%s\n",
824                   srv_sd->idx,
825                   srv_sd->upper_texture,
826                   srv_sd->middle_texture,
827                   srv_sd->lower_texture
828                   );
829           }
830         }
831       } else if(msg->ident==MSG_WORLD_SEND_SECTORS) {
832         CSectorsHeader *sectors=msg;
833         cnt=sectors->sz/sizeof(CServerSector);
834         DbgPrint("Cleint gxt %d sectors\n",cnt);
835         while(--cnt>=0) {
836           srv_sec=&sectors->body[cnt];
837           sec=GetNthSector(l,srv_sec->idx);
838           sec->ceil_dc[0]=FramePtr(srv_sec->floor_texture);
839           sec->floor_dc[0]=FramePtr(srv_sec->ceil_texture);
840           sec->floor_height=srv_sec->floor_height;
841           sec->ceil_height=srv_sec->ceil_height;
842           DbgPrint("sec(%d)%d,%d,%s,%s\n",
843                 srv_sec->idx,
844                 srv_sec->floor_height,
845                 srv_sec->ceil_height,
846                 srv_sec->floor_texture,
847                 srv_sec->ceil_texture
848                 );
849         }
850       } else if(msg->ident==MSG_PLAYER_UPDATE) {
851         ReadPlayerUpdateClient(connect,msg);
852       } else {
853         DbgPrint("Cleint gxt a mother fuckun weird resonse(%c)\n",msg->ident);
854       }
855       Free(msg);
856     }
857     break;
858 pass:;
859   }
860 PrintI("nomore",0);
861   MPClientSync(connect,l);
862 PrintI("done",1);
863 }
864 
865 U0 ClientPlaySound(U8 *name) {
866   CMPConnect *connect=FramePtr("Client.Connect");
867   Spawn(&PlaySoundTask,name,"snd",0,Fs);
868   if(IsNetworkGameClient) {
869     CSoundHeader hdr;
870     hdr.ident=MSG_PLAY_SOUND;
871     hdr.sz=sizeof(CSoundHeader);
872     StrCpy(hdr.name,name);
873     DbgPrint("Cleint sent sound %s\n",name);
874     SendMessage(connect,&hdr);
875   }
876 }
877 
878 //Returns true if should also be handled by client
879 Bool ClientTriggerLinedef(CDoomLevel *l,CDoomLinedef *ld,I64 flags=0,CDoomThing *t) {
880   if(!IsNetworkGameClient) {
881      return TriggerLinedef(l,ld,flags,t);
882   }
883   if(!ld->special_type) return FALSE;
884   CMPConnect *con=FramePtr("Client.Connect");
885   CLinedefSend lds;
886   lds.ident=MSG_TRIGGER_LINEDEF;
887   lds.sz=sizeof(CLinedefSend);
888   lds.ld_idx=GetLinedefIndex(l,ld);
889   lds.flags=flags;
890   SendMessage(con,&lds);
891 //This is appriate to send on client side too
892   if(ld->special_type->type=='Teleport')
893     return TRUE;
894   return FALSE;
895 }
896 
897 U0 ClientPlayerLineAttack(CDoomLevel *l,CDoomPlayer *p,CDoomThing *target,F64 range,F64 angle,F64 damage,Bool melee=FALSE) {
898   if(!IsNetworkGameClient) {
899     LineAttack(l,p,target,range,angle,damage,melee);
900     return;
901   }
902   SendPlayerAttack(FramePtr("Client.Connect"),l,p,target,damage);
903 }
904 U0 ClientPlayerSpawnMissile(CDoomLevel *l,CDoomPlayer *p,CDoomThing *target,U64 type) {
905   if(!IsNetworkGameClient) {
906     SpawnMissile(l,p,target,type);
907     return;
908   }
909   SendPlayerSpawnMissile(FramePtr("Client.Connect"),l,p,target,type);
910 }
911 U0 ClientPressAWall(CDoomLevel *l,CDoomPlayer *p) {
912   CWallPress press;  
913   if(!IsNetworkGameClient) {
914     PressAWall(l,&p->x,p->angle,p->height);
915     return;
916   }
917   CMPConnect *con=FramePtr("Client.Connect");
918   press.ident=MSG_PRESS_A_WALL;
919   press.sz=sizeof(CWallPress);
920   press.x=p->x;
921   press.y=p->y;
922   press.angle=Wrap(p->angle)/pi*I16_MAX;
923   press.height=p->height;
924   SendMessage(con,&press);
925 }
926 
927 U0 ServeGame(U8 *map_name,I64 skill) {
928   if(l) FreeLevel(l);
929   l=LoadWadLevel(map_name,skill);
930   InitLevel(l);
931   ServerBegin;
932   "Press A Key to stop serving\n";
933   FlushMsgs;
934   while(!l->ended) { //TODO check if won game and all clients disonnected
935     if(ScanKey) 
936       break;
937     UpdateLevel(l);
938     ServerUpdate;
939     Sleep(10);
940   }
941   FreeLevel(l);
942   l=NULL;
943 }
944 U0 PlayNetworkGame(U8 *at=NULL) {
945 //TODO level transition
946   if(ClientBegin(at)) {
947 PrintI(";evel",l);
948     PlayLevel(l);
949   } else
950     Exit;
951 }
952 #endif