From 1ddd9cd174663b2e14dc3e85a1c3f8afb188c303 Mon Sep 17 00:00:00 2001 From: 0xdeadbeer <64986162+0xdeadbeer@users.noreply.github.com> Date: Tue, 21 Nov 2023 23:11:07 +0100 Subject: [PATCH] 04: add boilerplate code for a collision scene --- 04-collision/.imgs/showcase.png | Bin 0 -> 3457 bytes 04-collision/client/Makefile | 28 ++ 04-collision/client/README.md | 3 + .../client/assets/player/idle_down.png | Bin 0 -> 1028 bytes .../client/assets/player/idle_left.png | Bin 0 -> 878 bytes .../client/assets/player/idle_right.png | Bin 0 -> 921 bytes 04-collision/client/assets/player/idle_up.png | Bin 0 -> 725 bytes .../client/assets/player/move_down.png | Bin 0 -> 1555 bytes .../client/assets/player/move_left.png | Bin 0 -> 1659 bytes .../client/assets/player/move_right.png | Bin 0 -> 1651 bytes 04-collision/client/assets/player/move_up.png | Bin 0 -> 1080 bytes 04-collision/client/assets/player/tilemap.png | Bin 0 -> 6736 bytes 04-collision/client/src/defs.h | 37 +++ 04-collision/client/src/format.c | 62 ++++ 04-collision/client/src/format.h | 9 + 04-collision/client/src/main.c | 278 ++++++++++++++++++ 04-collision/client/src/object.c | 54 ++++ 04-collision/client/src/structs.h | 48 +++ 04-collision/server/Makefile | 28 ++ 04-collision/server/README.md | 3 + 04-collision/server/src/defs.h | 31 ++ 04-collision/server/src/main.c | 216 ++++++++++++++ 04-collision/server/src/structs.h | 23 ++ 04-collision/server/src/structures.c | 55 ++++ 04-collision/server/src/structures.h | 9 + 25 files changed, 884 insertions(+) create mode 100644 04-collision/.imgs/showcase.png create mode 100644 04-collision/client/Makefile create mode 100644 04-collision/client/README.md create mode 100644 04-collision/client/assets/player/idle_down.png create mode 100644 04-collision/client/assets/player/idle_left.png create mode 100644 04-collision/client/assets/player/idle_right.png create mode 100644 04-collision/client/assets/player/idle_up.png create mode 100644 04-collision/client/assets/player/move_down.png create mode 100644 04-collision/client/assets/player/move_left.png create mode 100644 04-collision/client/assets/player/move_right.png create mode 100644 04-collision/client/assets/player/move_up.png create mode 100644 04-collision/client/assets/player/tilemap.png create mode 100644 04-collision/client/src/defs.h create mode 100644 04-collision/client/src/format.c create mode 100644 04-collision/client/src/format.h create mode 100644 04-collision/client/src/main.c create mode 100644 04-collision/client/src/object.c create mode 100644 04-collision/client/src/structs.h create mode 100644 04-collision/server/Makefile create mode 100644 04-collision/server/README.md create mode 100644 04-collision/server/src/defs.h create mode 100644 04-collision/server/src/main.c create mode 100644 04-collision/server/src/structs.h create mode 100644 04-collision/server/src/structures.c create mode 100644 04-collision/server/src/structures.h diff --git a/04-collision/.imgs/showcase.png b/04-collision/.imgs/showcase.png new file mode 100644 index 0000000000000000000000000000000000000000..bbb63ef84011213d1ce40637d85c3d843adae476 GIT binary patch literal 3457 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 Eg43Lj&j0`b literal 0 HcmV?d00001 diff --git a/04-collision/client/assets/player/idle_right.png b/04-collision/client/assets/player/idle_right.png new file mode 100644 index 0000000000000000000000000000000000000000..66a019baf47b4245c047cf03afff0377fd6bc342 GIT binary patch literal 921 zcmV;K17`e*P)Px&Q%OWYRCt{2nLTI|VHn5%J*qTG3Dy>kNDFD85EmQlA{FF7GuSRI(ksrH{GdZC zP9jAVBwa);4qb&D2yti}s**ul3M$nsXOJ4DEd;A*q)9c%Jq~HkTrRabwQO1P=g>^)^t8>Yf&fkOb6XqsJ>@QK!OTWGj+J7NbKd zMu$`sQ@Ut(>ISJnAE`kfp|lKI_oG89rPFBwK*f|^Zf8N50;+qutE&rxgM;jT2w4Sy zlI+!>4@o0yCF5GhaR9)ZlVRx3Q#*SOFrpD#@VgGQBe%SQ-0})y5AOnu!+;Y~gFZ5% z5duI)G(yFcPQ{c?v^qz$I!DEnPJX|iEqLx_upwz=2`DKbcyS6w)eEO4pCFUTh&rRx z9cu+Jq7h38<%_fDML@B5g4Xf{TFV!#pkLlR$GYrQBWx$QmM=h26u4bYF!TwGJk8O} z?W+Lu0gY`HXbptWA6B6#3YtA`OwS~(wn8=of9NQeD>hgZ&>9F404Rz*-==Azu>Kux zR};eL_j0)6fK34(QnLU6x2p*kE?q%?Shcl#jCr&MLR{`xux^Ato<4^6eLc8!W85|b z0HDR|<#fejTbu^Vd|q5&x5=7-efOU^o|&e}>4F2+1em6Y`N%k@$t__?Kv~*L(~~${ zG+^ix7#`?z%mX4|Q$Ws`gE!EPLNGIpzC5j zh;Svq>is`4cKJHMJP&Xrz#;wZ1lthUEO=elZSohPf_h^shE`tysJp$Px%k4Z#9RCt{2nm=e0Q5?s=DWWzJY4B1cB?>O$QUpZ`PEvrzyX_lQW$@Hl;~ z-QJNw%LJMLK{KDt+#YCSi7*wlgD|u%+Gt(0WAU8`0UW#{oSf+tpNSFxa7D=HG>Si# z0P3*$Wuz}$+52vn<05exz=pXEsyELbhBp3o@E0JAk9`tU^Mx!P+`AKK#0s9~qz^S; z$O6DnA(+8a9re)_0dR$22Ok$|duLa{Ug3LQo^~4SVn}W8>;eD`(?Y$`9566U3+d^* z3KO^ns7kel*9)(%H7u8k&fCeW1-|e77}~zPv`8L_4J!k>sXq!=nGoq1=^0Qi6@6!W zd1(>lQqea{qJVxQ;fx!9qIM~T0hVNTWwCl5*zz`ti1T^>ZEk194@HO0Af8X=@{MUY@17p)u_8O+84AUC;T&dRj%j8I5 zJ_0F(IM;i!Ae8-0Nz5+baJsfi^_MsS*u+*0`JMb`H@1LJUuolDXMpd$K)uoIioz&@ z9Adc^4{0}XcY?s^vpP|~45{Y@d&!x}*iYWE-obDE5&-Z-v)k@ZyHDl*pHAdsq%VRq znJFCW9pCPx)%}GQ-RCt{2n_FxYRT#&A(?X%Ug%r9Cv|Or?7E6S-h{hCSqTCFXmQ~SGeFH+2 zKtrOz2lW9S@PUL-Z?Va~5vgLbCRo%$Fd`rdX~7^X#gcAVN^!ShVRwr~c6`{K>CWyJ zc-b@K^!$>^%$#$Q^Zn0!=bLkx0f|H+kw_#Gi9{liNF)-8L?V&c9L0=EON(9VXnB3X zkSR8a#7g?diL4Cb;^(>URjo_4vlCcNztDy*Eq19-mO1WQ?vy`j%Sd2F{lXggUYJKwNxtQv=OS4Tb_* zn$|dp@}tg||7TcMzwijq+|+=3^L8pKD%kWwi=MuHuZQmLZWQ2~j&{*z3BzjoMLB?y zw_vSWTI}LEpuD`C%GImbQ}r^xFJHkUhdZcTy^8Xwe*y7clDA+jn6y%v1uUyyFVmT z-GkQ{>-sg+a3rb{AK@B~HgCIjZD%s-Z}YY%rwc>$Qm`71L=6eou3a|V-;KqUTQwNwkkJ{URMeZSxwZH=8_%C@Go4CeI-N8r^jaB^t=C#NrMPr0ds6 zw3F>Rf!gM6PYrcyZPPo3P-sRN6o?v*MAdL4susTQIeHW0QFsP+Cv zcb%T-Ub>toas#-RE;kgm>1#j_{~F2q8{Kt8qC8M2#{e^1-Uh&tKM{bU!a~Ct z;Oma>MVcg(KVN!^vV}`0-*{$pHE}^zIwNZ8G(HfC7ipa2{(+E|_FEn}JXV z2yA`-fy>(LHINqmt%5KIs0e&VMW6!!tubs zuroJ%&Ju|Z0Wf^$j((()hyq#P7ZAA_q%7LfVi?pN^z~&d=sU27q5fVGHVH-R*-l43 z0EaIR81ieY%K>o@A58uzbnC0D@caD$v|cy?K;_zP$wln4Gp0P&{E_bb=YHq}U~}V2 zUB4%xzs=ijSS-sn!q)-9ktjWHZ2+LRw-_3K7=9jPCV z*aSsUl#I@N#5a(ZCi+#7P4tf-VzHRv!YIGdovxSLdnj>by+f3fhe{jb*iK z($YkI7$a(`Wf0=7j{gtuJsbZU_UU=!^}M2dMN7BcG!RhN)J)KCLg5(4FU3~@we;Yv zyPWt#|7p}VKZYoYLx1?m*T*i7EQJmX*|F1)5DLfiBlY0}6i>?4F73M#crFn4@T!?3 z)h9av_U!!t%JEBgGJ5#0SK|5?&KjR7J9`ERtqs&UE@RNS9EzD?hm|n3O)RqxATb9s zdR}S>9Lv35L$--TB9TZW5{X12kw_#Gi9{liNF288*EP)Px*HAzH4RCt{2oLx*5R}{zpth!Y+fVEbP5<^LhYoh_R4=piatI(!2wvfpB+(2rI ztPjX)YlM^r6NNqiv9YP;nI#P*P1mM{6q^R=0|;(XN0Wm1L4pNi*RYG-nmv7(y~E6Y zpiMh>mb>?tY{Fb-GUwd$zvtY0XLbRJL?V$$Boc{4B9TZW5{X12kqAx(hkMY!IG2Y# zyh&;Q&AuTy9l6ha@}M#Uh$VuQ_n%7!i0y>+YwgZ_d0$+RNf z{im?sO91HBty@%I~&(Ei@-%B)mm1y=Vh5cR%`@N*JRuceHTB}8_|0(M~*EE}- zpHG)RI$&sXG+fd~fTQ6Ocbj;U?*0Q%I|0#IF95pPRzo-2Y7A{w+GjSg{wuB3q_kGk zd@w}w!I1F&>!H#1V_HYB4yZfXlrEh>qwTet-|VxYph$TC=`D8?DLtPX0fsApo}nFY9S z1OdS9@n5m+^+PCn?D6KaAaoYkP*6k_2TBbiem)qY?mnNPjn`brk__--Ru%x@ z;l!9W@`IrTD@?J3^-=%-IDOP&OZW?b9!`wmQtvkafcanunQK;aHo;mRPK+6jZ8l>F zGS{q*Ycms`0-$T1ZcL9109XuuXyFL};N#|Vc<`IRweLChIJ*0M02b#3btjwHb*91X zSpWfb~B#VbJ{sAkI91M%!!S8eeX?7}v&1;;F;? zO*abQ^GX<9>vRJEKKM!va97PNe!2G->~{4Xxf-`e3%$A^j;nw;4@NTf9>4I+a(1Mm9E!J% zhdq{HcnH$a&=B_=fUSigPVG+wZQ5B(n|2m!>wdQOXZ}x*4AAt*fN=W&8M+U0a&pkp z(qcG)%?U)OWY<=d?Ai)I&47_e#IW?+vhN_a>^lf>$beRC1W|0@8D%BK+EPe#yk0Nr z>+7*@Z6<*DEnx`uN*IGY9uEK@5C|X;2mnB`%^(rW0J&T)?I1fl8-ThQ#4>I=L0U63yqD9Xl`zf8vr~-z;!3k02cV` z*RLl%=iM?VFLQX1w1g>&q6PoNnX_6M&}RVvrca8^6o6sf&nAB?_-yn)i^}p+mJOEM zrVth8twdqoN+2o%uUGvB5KI5b(2L+mMY(AP0_N=m(wjS1A=|FTtZ&@-wdmO3Gxf)o z0$mwcZovltJYG8dd&3+YycAP(d z9sp2PRRtjaIe08Zc`FcDAWY6ZJ4>W0lXDS6@Bvi}lXDTYef9}}cp+r2S&jFLUcjFV ze5O2RjVuCyf{iw8+qgn&{OQKu`uoNzTDt}SDC&Py0LwB1L?OKh68OU+odJLn1^>#; zaO^ez8>atA6krrWR0PTHn@IM{IrxlNzmoc|7t7`x8-{*=y43mq8E!ul$42(V?6M!x zX7jvaS#}7!t0$32Boc{4B9TZW5{X12kw_#GiA0jB`~&XJdZ1hTZ_NMz002ovPDHLk FV1h634Px*ElET{RCt{2n_oyAM;ymLcNp;lm0lpkq=&NedtqA zT1d1H{z;mkQiFk1^Psk&C8Dq+ivLj7 zsyZ4;+UU2UafQcS@UIJxE3;rP6Qq@Xvl})^{JPcI+VTB`ubEQdDBX2B@vENNMDX4W!Fw|d zUitv+ISgoIs(#^d7YL=hP5?sbt`lLuSA_juA?7X%F?U&n{a#U6SZGV}q@jMZr-6=f zWmZ762e?}Zgqr>E_f{#7zvKVp6%B<2@FLg}v4dV>0~anD!LwBGEg7dLzA zGfKbMP_#ocfcly0J!;o!Um~L6c!#E&SkR|>k7bH}l7b&vToO~gM}^|;5f#VUY~2tI z$2$Zm+5iyB&^hsPV~4HvYrQ|TxFm@6hS1`YW`Z|9x@7AH0K~9wR112d3Emv|!V>+a zJp|tMPvCUwXPuEq1gF!(;EX zx(cST5LyHvP`QiW?_337y+!&>Gl0|SB-fRbbS@sXF*1Stq3?jG2>>n*f2!&?H47MR z0MT%~1Bj0I0D~jGWxs=s9|o%{%Xz!=ED$xp=Ffj*ejx?@SKm$m5L#R!*OkM}he_a#Rzi~``>eTXaljqGgb6BxW> zYf1)KQ&ht2#0c)%W;1S=_EeOWa@RjWePb)(@Wb~5eFOafS|%_9)7AlU3kp%ZJ-DkX zANw5e;#=>lhj!Tzad`SJi}A)iNf+jBPw5_nuk^z}eL3Oa96KBOz$R@VW+unP%;cDE z4M1H51jNRzrDEgOQd>X#5qr2*^+zHRZ9kx;rA4?fWKdJcA=X zD#}VzUW*yQwkv@G^lJdmpBn%mFE39!S6f@Fm9-g3nzp_7F#y3}kYF$fKx=Dj%Cbgc zh5?d-50LB1p|i7-_V#u)@ODI9|6jJCY67z{d?gU{!9VoMe(kuuy&V9*-*0d=EI|zW zMp-WSfk1#jAOJ+Oq1jo$NCTwo`SCQjok==plVL#D>#HlvliDG)xI`opVQ6Rw&<}-X zVrT|P6s`9DTvv`3{N}bZC`#h1ks7v|8opZLdE~Y0*RKQS8^KTmyjE4I9ml8sRH(y1 z+>Ah!Y|5f!Qx>c|6F90X%c-s`U$$U;7EnzPHw+lQE@0F;fY_4nWOII&&IF;wrIdx6 z+s@E)`Xq{?uqFSYCm$?E462?fH~^8#1c}??>YGtDH8r~PRVC3igJ4+j&5ged*%vT; zA*k$ysED;L2f41CBop+UKB*nY2Vdov-&V8<*plxg&*ji{gvW8})F~hu{9xGl#AoEn zlTd@NvexC`v#Woq_f^%!*FnUaAQt?UydJLUal};InbQod(@k8P?Ay9ZyKr~z??-*- zf5GU+6{XK*@#`NCd+{Px&@<~KNRCt{2oZm~6Q5eU+8##ZJ7t$=5j3AKk#?YjMGRPk=6l8XjmtI9k(oG=I zg?JGmTfUh z0EMu#8^z9URJ0hv5UIZu4xp21gpJKDfs%+pg#J=F0FX9tBh3olb->m ze0~6cx2GF3b1!l_cze20QSJcLj07#Rw~yKR#heFyp3YSJKz}cw;2A(09vQ{Ow)2N} zq%;6AsQ|DdxV(bN@iBnF2IO^s;PQ%TySJyCSQ7c@^K|C5Pq84Y1P+gk0)Xz>2K33- zmES*z0_vRww8-8*0P`T|^K=48+b3x+H7kKu(M}x!)L2cv2!xe_9S#Q^4u`=R%vJ{# zkx5T!?*bIt22#tv8Ncq{AQZ(2nF9>EwlV111^~=0yf!=w-?^UpJUKp=^IR2X=NC<* z|HQo~hVMfy)fj52CXtDB1vq!>k!gF~{imtVq3H=+IBCQ7rvQ~$BNj&=Y*Myh0kT9XfC=#+UMki7R0hENC4Cw~6r z8vtVS;ZjBT3UIZTFM)JK1b}b{$RxWU!W18=8~{k215_yiXJh&I03rf_`t@+r48UX? zQ0)MyV;ku2wRG;t-ae`-D>SH_1{TmqRR;hh)1UkwM3`~8q0F!u1-`_{B7hj}QbU=c zVQDQUNR$NRTR?xa@m+Y|Y7~LBSlS6nqaq9-J^9{dJ=CaMqn3 zK46Rl6{8;jY)fk~(=FiRdP4sucbVDa;t51L8lata8WWFiTX7L`$b?XvNS6jOw8)SJ ylydazuuT|-VHk#C7=~dOhG7_nVHk!f3V#6wOnMXx#h;)60000bqDFOijk={WhBq&vs-lRwo0qN2rHFS^; zf>MM4Dm`>UN&Ce=;``3d?CzN}vvcn=&)j+L*;uHt{zZnX3;+OJG&InC007jKS3r}V zmU6F@XF&h}DDt_E4%ARbN5I?H%lWyx698OGf+wjNbZc@&VT$kSNitbn_8EZocFIVs zSd~K~7z!+0raS*kYPd`$)_+xDsG^}maFjF2j5go?%J6E5Ii@@=r7$k_)aHT9rQf{$ zYON=2vOyS-c3Y1^xiEuENbmi=rw+I4Bxr3XUW2k;b7j1D8a8}D_2KsxpL#n+FE~AD zA@8H*$m0upu|1~Fnd~bUSKg1j>3or=(!)@DH`G|wev&4Zzp3ggq3_miO~*|gXNg-T zJVG3Y9u@WSl9^Zc6@09w{RcTGWy*6_+52&?Y3(!v;4 ziJ0-K6bC`WHF;)o&F_qXPl@I9|Had!7H9<$mJoX_C<{;5Nh5|nKK?Ot@>quKX$k^)G7T;({=TN6QZw!yyN-y39W!&i)PZ7+n6+U|oY2w)n`wx=} zI`<0SQABx-_~obr*Eh{qXjN3eeFv#+JNr9TxOSrT_iYSwn}BbR_l4`jly#wXxTmiR zkpErzZ6zs`7KpdOBR>FOWcyb^KvoVHrIF6x&`6JNjgg&Q;uaBfBbU;2#b3|LU&qVC z!^zVh(D8M$_jhs>2zu`CBA{<*1houj;sgLLaYJ1#v*0;wF2dW)JiQ;i5Mj_PtPAO8 zrcrX#dMYgzPd)q*8~v`q;|;$|LcTOt#M?9cxL zh1*}qr;dn7OP@}31R%-um4{mem|CkR9bMCCe2&8y3M zXinKJu6d4M!-WrOnL3e`OYLd{^?hI3D?fTzzHA7NuE1=>3kUO%74uTArk_|3tUB4YqO50ZF||%b%2oA zF{;dRL3acWk!yKMkEKhSt40#dIXbrSEj7g74p>0@Cf`juxv`?y4yq4d{k}w2E2M{{#6uhiqO9xb3$CNza z_kV=9ZYiUNPJlM?Feuvb6E*TKj+5%^HG~kQ=iPcsdfN%yqaoY=tONL<7(!y!P8QiK zd3o^AuKw%~J`RdHQJliUr7QT&_Q++k7)u+mwI;)^YOvejiT#h-pBLfz33O9z`Blyc zH_I)NLMp3ry#MzAqShWh%autx5*$M#*A=l7b4fTcBctT)lI1LkfdN+Ox$9PojZ9E` z+ttsf6sq-v4bDPn@54O<-H&Z;Rej4PU$|h^0bywaQ~aK*AwB}`i4|Xzb3c4UasG*# zX?&8k^3$_KyN^pdra6FVHdzYkbcfgPjcKcA+;Yf%v$Xf{^}b!5^T>azLq!vm&~+e) z?i;Ag_BqzQWzni&9mb(iruiPLX){8yko(f%tA@{#Z8(k4T#0`51rUpUdHj85%`hJ4aojI>! z*l8u^UJ{SSfoU?y&JCLlC4AmDI~S>tNhPr zB}{08Juaz64Mtd;)#@+RrxKGKt-PzJKXGJ8IROKknd$!~j(#(nMq`SS!sCZP3xe&uRBgPMxo6rErFBV0d`y->mjO5%^II^wA^3#)YzZJ10=Z?(4a^ z$Mu|aDxf#KxziJO8J8ZEFn;H4vgco~@}ttNUl@_L1v=i39GbCpJpTMW@^cFH8c}?X z+9X1~*^{Z3LHV=X>P5)7rRYr%5HyOo*8nzn1gbazIS{rcY*01CX_gM>Fim#5(nBJP zk57LUyhssR1a{fGxpnN5R8MSpP;!rvR;T-3P_F;{yLKJUHRql&jk5aw6t7=7U z#9u4DGc~@kgtn&MIEpP447+UFX_-f@E3}tVBw?=(*G#y>$g{iRTln*0=RvzHSBd== z={xe?OL7FyA>$Wqz4K)eWKMVi3cZjnAkkSwv-kRt!)@n4aX{kNH6jw$Jt#W91%6ZH zxJd2Q-5_xqZ-Y;CL3O$|IBfoj9Uo)sf+T8D>rT?AvI4IG5^T-$yR2TbapaqByG+|W z+_;G7AfKs-dKc&c1J#XFMi8NEG7Ui1KXMyjT!NVk56_+v!-B1=1te7g zgIOoP!QOqWZ2l846_PT-U`X}`Jz|-yu_=(7f#&k(O4W#pE~C9hTlu$i@V6}TuYIXf zmE7JPL4-h)qW$vE5yN@ZEQWx_4bkY(y&*mgj@jS+f!B}$l0tXY=so6iQiS3Yc8uWx zk)HutznRhv{txd8PVVbq_q*@b z+n^6VCIUYw)GMv7k~2HyN%56y?0=*p_BY}kGmXS@G45vqb1l{Bsr`kvXOCe!biH=# zgIwm6_3GMDI&c1*BJSY1?=Zs}a>aq>FW6;}yT6;&AqnWMCCa~KttlmCJE61*jQje* z+$_a+e1$FWC29%~%CGV5?k`V(vHa zwe<6nhabSdY#Z`iX=d*4F-(*rOmcBO=s_0F4$}#DU^{r%j@pu(3JM;a^og%wMS%r}^E;(q5FNYh>5SS`MoZNyd4@pL8Y?&v<} zE`lRa+8<)Zo;<`nAOZl3i@5e0^o@BEt4CnBNudE5lSwY$I@i%yPGhM98-VJIv+lOG zVc|e{rEP(HxTz$FVH#+Ru(khbAO`Siz2Jt5&{u~=NRu}=7~b!_V)*_&X94+7rz?{Pb~4Gf^)biHox@5O2?0Qc<8-SFZ$EKBq>T zzZddHYme7Xd@*ypM`kpoD_0MCJ%cm7swYxbGIcZYk5jg=XqyNYlcd2mr+K6$)d=d9 z0tNd***@@0fq|K(HQA`KtG1b<7%!_Pk!`;>>*YjDo64P)4V9@mcMBr9_u%J6w8qVI zXVsfo#^&p1`nxH{8du}1vUTnfV#@m8^JXXQ_;uS@yxNF=V&3@D1;4rwXRQtqd9{+7 zK;c-?dY$M|SCjBd?~Ol#@9CjzXyMVYSC^gzU5W541!!IJzzTna6&q3iT@L+AH)r#g zkcS9k0up!pgcsJt{!&Nmq5Th7MtNtSvXEPFmR!b=mcJ>zGs?TRwU8RV{UzC;N03Rl zXU^=GS(clnGa6jq=cm;|-U%plzY{B#1Rh0l8&xw~?H7mc%Gka3AxgX4_R+l3@1n#~ z>a#I4=8aj(cBCm%n`Br`VI^x!@kIB-p|?@jdNgjS?z~>l&AOyQdYcF*%ewnv`-WdI zesM$41Y)Z-&!Q21kvjAHy5Cwd_boKR&?gWswIG{lj1L;c!FQsiz%$Pnl}k`KSgY@dq*o`UGI z4}TA{V*k@*u+`If&{&o&vkk7vW0=~d^i=aRM3ea6OcaB-C-0vT?obWN!6h}V4CB3QwoQA3o=b%`Qr`a=A1-Z+7{TFgdS#``=3n(*( zlsNbN!NkRxwn$L`R;&Ye?4ESHHYB%S+54vTuIB95*Q38irjW3@-G|H3S-(w9zyOB? zw(+7tcuNKNdHk=yU24klBeHfuu+Ey|1u}4FfbYUU;6NNDs!8M^Jl=mL<#RllRQBHA z`WVIC9`6A(jt+;9`Ca&ukR%zW zC8Pvq(|fQlTkf60VPw~H`|Mquhq$`}HUhd&ArJNL+2dsD_K*FcW74*7E4oBQ$^|AG zW0Ma#yHG){v=ldhySXy=b5%_lG_18{kI<6bRSz#-jzou30HbxhGY(zWwOAHQy=`Mq zKd(J+q67g1BuVGr`8cGkkkjRsj1wjSJXZgW*;MNZBNuin_1tuLzlD!e#DOB?2U=kG zWhleu))olxq8+1=4QQ5|OJWQC*%*%0eqJ8`~HvPu??hs&>3T9iw@qQ_|3 z5L%2C^T{^hboms8GT{4XW&QTRjHV(j7~xt-Tv2B!pMFC*ehQ~Hw6laQtD<^@F1^`` zFqXR?k#Is{2lmE^;Y8!-XL?hw@G5=3HL{jUfM>M)Mk^ z%ap3%uNU#;*cw5R4=Y=RR}M}f!d`ShYOaXQHY7gW=)rmsmL!`t$1LDYkOy*taS)*6 z;P24&I8vZITgUSRb^TFt!iN@PKQzh$MI&|*aGWnME4xqLi~epdLPSj4#e;y=`v@qm zlf#Cp2w}ye1)>KYKX|~JE^8OhX%{L#tPIuP-HqHE@%^LSL%SIU#e|s>epG|E8V48l z!q!>)kqrCQ)(mw}w(xTzh9Ut5PbiVOSRDAoK3rk}Nm82j3K!hmp7$dqAC2r5mX^{x zIXMl+v{Qs<)vMC~K7VKTsrHh)B!Jk|_lvW%=-Ara1OPi8Qn|f5REjF5rFPiAQ-DPtQ#KaPx|RkQhs&8sO5&q;8&-1@q z0tdc+R(zWRAHbhyaB};yIi$%?_rADH2(9P7l8EO6Sg}g8fraG)R{`lPVNwJa6qH1u zfBg7y+EEav%o6|zC7rGLs$Z)Wa@BOFW;kXxi*>G|gVTPC)@HD?gF*!)m&#^rp-?DN zFOI{0WAc+d1SXY&lsOiGv5$5?OBK3L*xC)Ss&}~rl(ezc(jXBQCiX%wjwFmRSyFDJ(aa@)d>&AU1K-f@d#;zW?aic=jF z^DjCi!sZ(y894{bs>2ph8Q^tR5G$PJXdheH^_5fJ&IJkeU?~^rn47*W7R(R4C=vGz zGnaz6TGF$+wLJfe(cOzOr!A#vAFtGq5D5!e&dbPVQ(~edBugiFQ(Ie^UX8z`br<}- z!Hyz>X)mYb0-xV~S6D%=6p2z}CAE&QRH}N??s8hu-p*&Dc6^xv;X#=8@bSTc@As#g zlnkePl-|KmvxQ}mq3C_(5h{bFR|palKm}{&`H+!KO^GsRR10mpmuPC#1qE`zk0|O; z(AN1S|Mdp7sUJe!DAA*2HUw#?x(geK%Fs<*k2BC}tEd>RAx6(gXqv%+CwCxG!9oHF z8o5dyS*^GLBi{C^&(xY^C{VYF7En{;-PqW0R*&ckr5L+v4^L<@Y%hE-xnOgufBQH# zIH?CpWIo30;sqQCfh`NP7X|8}>xTuNM{Sz&8ng2~v(=tgBXFHK?7Y0+>U}_SHIeT$+WI8`s#>1u#J;*>X?r~O(`^8{uAFS7EL6N}BI za60)dF`)4#58So!Kr|V7bmSkb%2)BYfg%2FN7E8tUR0ZSOM{8Rj9vZE3spJguz-HW zym94Dp{j=m@EefHUB6xWh4-gs=h`nt4(GmOxwDCrpwdFV`J-DEVion2cx_*9Gi)@jOiczD50_e_>ar+7z8D(zhK((A#zf%=zS zx#9aCQejCZq?>Fc-WalHfP zE~dkkUVdLz5Al+O#*C-|iPxwG&b9_(`peAA0pRTH?8dTJ!f>bB5@eLdi<=jpefFY_ zD2sBXwVBGjwXn_{x6z$ksj{%1;iI8&sfHp=h2JYL`@SW>D#YKf4!qSj&mW9F>h$rp zI4^Yy!-@fhfszzVIK=jjsGSUa1x)EJ624VZW!^$=-VBGyxq6!jUTA?g=)@qj9=y`S zD2+H)aF@~QmZ{g;I6PrIDi;Q?wRfgF;OkgI3j%*8#Nv0GEiP4~PyLF}8|mmT)s1IjiP#&8N{1A!ILkJU1||0DVhc zjAMS6qFlSOFzeG%WD~-h|2~$?{h3-iclHXH?j(RY%w}FukE-DAQ|%#Aze)~zJE76z zsy)73rM&@UXfd-=B?{8&Y0J97y&Byl zJH3}~%}u|SPuyhA6>0h-Bj-6!7dQIca0z-szt}ygocsItGGSc5pVaI} gvj2Z8B*dwm^^)yQNUe!B<-Y=8sAsHOr44)aKMh*|cK`qY literal 0 HcmV?d00001 diff --git a/04-collision/client/src/defs.h b/04-collision/client/src/defs.h new file mode 100644 index 0000000..df4f058 --- /dev/null +++ b/04-collision/client/src/defs.h @@ -0,0 +1,37 @@ +#ifndef DEFS_H +#define DEFS_H + +#include + +// Server details +#define SERVER_ADDR "127.0.0.1" +#define SERVER_PORT 9080 + +// ERRORS +#define STDOK 0 +#define STDERR -1 +#define MEMERR -2 + +// SDL2 +#define SCREEN_WIDTH 1000 +#define SCREEN_HEIGHT 480 +#define WINDOW_FLAGS SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL +#define RENDERER_FLAGS SDL_RENDERER_ACCELERATED + +// MCS: Message Communication Standard +#define SERVER_MESSAGE_LEN 10 +#define CLIENT_MESSAGE_LEN 7 + +// MFS: Message Format Standard +#define PLAYER_CONNECT_FORMAT 0 +#define OBJECT_PROPERTIES_FORMAT 1 +#define PLAYER_DISCONNECT_FORMAT 2 + +// MSCA: Message Communication Standard Actions +#define LEFT_MOVEMENT 0b10000000 +#define RIGHT_MOVEMENT 0b01000000 +#define UP_MOVEMENT 0b00100000 +#define DOWN_MOVEMENT 0b00010000 +#define NO_MOVEMENT 0b00000000 + +#endif diff --git a/04-collision/client/src/format.c b/04-collision/client/src/format.c new file mode 100644 index 0000000..afbe628 --- /dev/null +++ b/04-collision/client/src/format.c @@ -0,0 +1,62 @@ +#include +#include "defs.h" +#include "structs.h" +#include "format.h" + +extern SDL_Texture *idle_down_animation; +extern struct object *create_object(SDL_Texture *texture, int scale, int resolution); + +int handle_player_connect(int *message, struct object ***map, int *slots) { + int object_id = message[1]; + + if (object_id >= *slots) { + *slots = object_id+1; + *map = realloc(*map, sizeof(struct object *)*(*slots)); + } + + if (*map == NULL) + return MEMERR; + + struct object *new_object = create_object(idle_down_animation, 4, 64); + if (new_object == NULL) + return MEMERR; + + new_object->id = object_id; + new_object->x = message[2]; + new_object->y = message[3]; + new_object->colliding = message[4]; + new_object->force = message[5]; + new_object->state = message[6]; + (*map)[object_id] = new_object; + + return STDOK; +} + +int handle_object_properties(int *message, struct object ***map, int *slots) { + int object_id = message[1]; + + if (object_id >= *slots) { + *slots = object_id+1; + *map = realloc(*map, sizeof(struct object *)*(*slots)); + } + + if (*map == NULL) + return MEMERR; + + if ((*map)[object_id] == NULL) { + struct object *new_object = create_object(idle_down_animation, 4, 64); + if (new_object == NULL) + return MEMERR; + + new_object->id = object_id; + (*map)[object_id] = new_object; + } + + (*map)[object_id]->x = message[2]; + (*map)[object_id]->y = message[3]; + (*map)[object_id]->colliding = message[4]; + (*map)[object_id]->force = message[5]; + (*map)[object_id]->state = message[6]; + + return STDOK; +} diff --git a/04-collision/client/src/format.h b/04-collision/client/src/format.h new file mode 100644 index 0000000..f84065a --- /dev/null +++ b/04-collision/client/src/format.h @@ -0,0 +1,9 @@ +#ifndef FORMAT_H +#define FORMAT_H + +#include + +int handle_player_connect(int *message, struct object ***map, int *slots); +int handle_object_properties(int *message, struct object ***map, int *slots); + +#endif diff --git a/04-collision/client/src/main.c b/04-collision/client/src/main.c new file mode 100644 index 0000000..9914a22 --- /dev/null +++ b/04-collision/client/src/main.c @@ -0,0 +1,278 @@ +#include +#include +#include +#include +#include "structs.h" +#include "defs.h" +#include "format.h" + +#define DEBUG 1 + +int game_running = 1; +struct game game; +int movement_speed = 10; +double delta_time; +double last_frame; + +// network +TCPsocket server_socket; + +// textures +SDL_Texture *idle_animation; +SDL_Texture *move_left_animation; +SDL_Texture *move_right_animation; +SDL_Texture *move_up_animation; +SDL_Texture *move_down_animation; +SDL_Texture *idle_left_animation; +SDL_Texture *idle_right_animation; +SDL_Texture *idle_up_animation; +SDL_Texture *idle_down_animation; + +// scene +int objects_count = 0; +struct object ** objects_map; + +void prepare_scene(void) { + SDL_SetRenderDrawColor(game.renderer, 96, 128, 255, 255); + SDL_RenderClear(game.renderer); +} + +void key(SDL_KeyboardEvent *event) { + if (event->repeat != 0) + return; + + if (event->keysym.scancode == SDL_SCANCODE_D) + game.left = !game.left; + if (event->keysym.scancode == SDL_SCANCODE_F) + game.right = !game.right; + if (event->keysym.scancode == SDL_SCANCODE_K) + game.up = !game.up; + if (event->keysym.scancode == SDL_SCANCODE_J) + game.down = !game.down; +} + +void handle_input(void) { + SDL_Event event; + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + game_running = 0; + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + key(&event.key); + break; + default: + break; + } + if (event.type == SDL_QUIT) + game_running = 0; + } +} + +SDL_Texture *load_texture(const char *path) { + SDL_Texture *texture; + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Loading %s", path); + + texture = IMG_LoadTexture(game.renderer, path); + + return texture; +} + +void present_scene(void) { + SDL_RenderPresent(game.renderer); +} + +int new_key_position(void **map, int slots) { + int slot = 0; + + if (slots <= 0) + return slot; + + if (map == NULL) + return slot; + + while (slot <= slots) { + if (map[slots] == NULL) + break; + + slot++; + } + + return slot; +} + +int handle_server(void *data) { + for (;;) { + int message[CLIENT_MESSAGE_LEN]; + int recv_len = SDLNet_TCP_Recv(server_socket, message, sizeof(int)*CLIENT_MESSAGE_LEN); + + if (!recv_len) { + fprintf(stderr, "Error: failed receiving message from server\n%s\n", SDLNet_GetError()); + break; + } + + if (DEBUG) + fprintf(stdout, "Notice: received '%d' bytes from server\n", recv_len); + + if (message[0] == PLAYER_CONNECT_FORMAT) { + if (DEBUG) + fprintf(stdout, "DEBUG: PLAYER CONNECT MESSAGE\n"); + + int ret = handle_player_connect(message, &objects_map, &objects_count); + if (ret == MEMERR) { + fprintf(stderr, "MEMERR: Failed handling memory for player connect\n"); + return MEMERR; + } + + continue; + } + + if (message[0] == OBJECT_PROPERTIES_FORMAT) { + if (DEBUG) + fprintf(stdout, "DEBUG: OBJECT PROPERTIES MESSAGE FOR ID %d\n", message[1]); + + int ret = handle_object_properties(message, &objects_map, &objects_count); + if (ret == MEMERR) { + fprintf(stderr, "MEMERR: Failed handling memory for new object properties\n"); + return MEMERR; + } + + continue; + } + + if (message[0] == PLAYER_DISCONNECT_FORMAT) { + if (DEBUG) + fprintf(stdout, "DEBUG: PLAYER DISCONNECT MESSAGE\n"); + + int object_id = message[1]; + + free(objects_map[object_id]); + objects_map[object_id] = NULL; + + continue; + } + } + + return 0; +} + +int connect_to_server(void) { + IPaddress ip; + if (SDLNet_ResolveHost(&ip, SERVER_ADDR, SERVER_PORT) != 0) { + fprintf(stderr, "Error: resolving host of server\n%s\n", SDLNet_GetError()); + return -1; + } + + server_socket = SDLNet_TCP_Open(&ip); + if (!server_socket) { + fprintf(stderr, "Error: failed opening socket to '%s' at '%d'\n%s\n", SERVER_ADDR, SERVER_PORT, SDLNet_GetError()); + return -1; + } + + SDL_CreateThread(handle_server, "server", NULL); + + return 0; +} + +int main(int argc, char *argv[]) { + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "Error: could not initialize SDL\n%s\n", SDL_GetError()); + return -1; + } + + if (SDLNet_Init() != 0) { + fprintf(stderr, "Error: could not initialize SDL net\n%s\n", SDL_GetError()); + return -1; + } + + game.window = SDL_CreateWindow("03-network", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, WINDOW_FLAGS); + + if (game.window == NULL) { + fprintf(stderr, "Error: could not create window\n%s\n", SDL_GetError()); + return -1; + } + + game.renderer = SDL_CreateRenderer(game.window, -1, RENDERER_FLAGS); + + if (game.renderer == NULL) { + fprintf(stderr, "Error: could not create renderer\n%s\n", SDL_GetError()); + return -1; + } + + // load player animations + move_left_animation = load_texture("assets/player/move_left.png"); + move_right_animation = load_texture("assets/player/move_right.png"); + move_up_animation = load_texture("assets/player/move_up.png"); + move_down_animation = load_texture("assets/player/move_down.png"); + idle_left_animation = load_texture("assets/player/idle_left.png"); + idle_right_animation = load_texture("assets/player/idle_right.png"); + idle_up_animation = load_texture("assets/player/idle_up.png"); + idle_down_animation = load_texture("assets/player/idle_down.png"); + idle_animation = idle_down_animation; + + if (connect_to_server() != 0) + return -1; + + // game loop + while (game_running) { + double current_frame = SDL_GetTicks(); + delta_time = current_frame - last_frame; + last_frame = current_frame; + + prepare_scene(); + handle_input(); + + uint8_t message[SERVER_MESSAGE_LEN]; + message[0] = (game.left << 7) | + (game.right << 6) | + (game.up << 5) | + (game.down << 4); + + SDLNet_TCP_Send(server_socket, message, sizeof(uint8_t)*SERVER_MESSAGE_LEN); + + for (int iter = 0; iter < objects_count; iter++) { + struct object *obj = objects_map[iter]; + if (obj == NULL) + continue; + + if (obj->state & LEFT_MOVEMENT) { + switch_animation(obj, move_left_animation); + idle_animation = idle_left_animation; + } + + if (obj->state & RIGHT_MOVEMENT) { + switch_animation(obj, move_right_animation); + idle_animation = idle_right_animation; + } + + if (obj->state & UP_MOVEMENT) { + switch_animation(obj, move_up_animation); + idle_animation = idle_up_animation; + } + + if (obj->state & DOWN_MOVEMENT) { + switch_animation(obj, move_down_animation); + idle_animation = idle_down_animation; + } + + if (obj->state == NO_MOVEMENT) + switch_animation(obj, idle_animation); + + obj->animation_speed = 6; + + draw_object(&game, obj); + } + + present_scene(); + + SDL_Delay(16); + } + + SDL_DestroyWindow(game.window); + SDL_Quit(); + + return 0; +} diff --git a/04-collision/client/src/object.c b/04-collision/client/src/object.c new file mode 100644 index 0000000..6be16b5 --- /dev/null +++ b/04-collision/client/src/object.c @@ -0,0 +1,54 @@ +#include "structs.h" + +extern double delta_time; + +struct object *create_object(SDL_Texture *texture, int scale, int resolution) { + struct object *object = (struct object *) calloc(1, sizeof(struct object)); + + if (object == NULL) + return NULL; + + object->texture = texture; + object->resolution = resolution; + object->animation_speed = 13; + object->scale = scale; + + return object; +} + +void draw_object(struct game *game, struct object *object) { + int texture_width; + SDL_Rect src; + SDL_Rect dest; + + src.x = object->animation_slide * object->resolution; + src.y = 0; + + src.w = object->resolution; + src.h = object->resolution; + + dest.x = object->x; + dest.y = object->y; + dest.w = object->resolution * object->scale; + dest.h = object->resolution * object->scale; + + SDL_RenderCopyEx(game->renderer, object->texture, &src, &dest, 0, NULL, object->flip); + + // update animation slide + SDL_QueryTexture(object->texture, NULL, NULL, &texture_width, NULL); + + object->animation_clock += object->animation_speed*(delta_time/1000); + + if (object->animation_clock >= 1) { + object->animation_clock = 0; + object->animation_slide = (object->animation_slide+1) % (texture_width / object->resolution); // clock arithmetic: jump back to first animation slide + } +} + +void switch_animation(struct object *object, SDL_Texture *animation) { + if (object->texture == animation) + return; + + object->animation_slide = 0; + object->texture = animation; +} diff --git a/04-collision/client/src/structs.h b/04-collision/client/src/structs.h new file mode 100644 index 0000000..fa37e25 --- /dev/null +++ b/04-collision/client/src/structs.h @@ -0,0 +1,48 @@ +#ifndef STRUCTS_H +#define STRUCTS_H + +#include + +struct game { + SDL_Window *window; + SDL_Renderer *renderer; + + int left; + int right; + int up; + int down; +}; + +struct animation { + SDL_Texture *tilemap; + int resolution; + + int skippable; +}; + +struct object { + SDL_Texture *texture; + int resolution; // of the texture/every tile + int id; + + int x; + int y; + int scale; + Uint32 flip; + + // animation + double animation_clock; // everytime it reaches 1, the texture switches to the next slide + double animation_speed; // how fast will the clock reach 1 with respect to delta_time + int animation_slide; // the current slide of the animation + + uint8_t state; + + int colliding; + int force; +}; + +struct object *create_object(SDL_Texture *texture, int scale, int resolution); +void draw_object(struct game *game, struct object *object); +void switch_animation(struct object *object, SDL_Texture *animation); + +#endif diff --git a/04-collision/server/Makefile b/04-collision/server/Makefile new file mode 100644 index 0000000..a606484 --- /dev/null +++ b/04-collision/server/Makefile @@ -0,0 +1,28 @@ +CC=gcc +CFLAGS=`pkg-config --cflags sdl2 SDL2_net` +LDFLAGS=`pkg-config --libs sdl2 SDL2_net` +TARGET=server +SDIR=src +ADIR=assets +ODIR=build + +SRC=$(shell find $(SDIR) -type f -name *.c) +OBJ=$(SRC:.c=.o) + +all: $(TARGET) + +.PHONY: default +$(TARGET): $(OBJ) + mkdir -p build + cp -rf $(ADIR) $(ODIR)/$(ADIR) + $(CC) -o $(ODIR)/$@ $^ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +run: + $(ODIR)/$(TARGET) + +.PHONY: clean +clean: + rm -f $(ODIR)/$(TARGET) $(OBJ) diff --git a/04-collision/server/README.md b/04-collision/server/README.md new file mode 100644 index 0000000..c3ea17a --- /dev/null +++ b/04-collision/server/README.md @@ -0,0 +1,3 @@ +# Collision - Server + +Server-side code for 04-collision diff --git a/04-collision/server/src/defs.h b/04-collision/server/src/defs.h new file mode 100644 index 0000000..3294e62 --- /dev/null +++ b/04-collision/server/src/defs.h @@ -0,0 +1,31 @@ +#ifndef DEFS_H +#define DEFS_H + +// DEBUG +#define GENERAL_DEBUG 0x0001 +#define PLAYER_DEBUG 0x0010 + +// ERRORS +#define STDERR -1 +#define MEMERR -2 + +// MCS: Message Communication Standard +#define SERVER_MESSAGE_LEN 10 +#define CLIENT_MESSAGE_LEN 7 + +// MFS: Message Format Standard +#define PLAYER_CONNECT_FORMAT 0 +#define OBJECT_PROPERTIES_FORMAT 1 +#define PLAYER_DISCONNECT_FORMAT 2 + +// MCSA: Message Communication Standard Actions +#define LEFT_MOVEMENT 0b10000000 +#define RIGHT_MOVEMENT 0b01000000 +#define UP_MOVEMENT 0b00100000 +#define DOWN_MOVEMENT 0b00010000 + +// OC: Object Constants +#define GRAVITY 2 +#define MOVEMENT_SPEED 10 + +#endif diff --git a/04-collision/server/src/main.c b/04-collision/server/src/main.c new file mode 100644 index 0000000..531f7f8 --- /dev/null +++ b/04-collision/server/src/main.c @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include +#include +#include "defs.h" +#include "structs.h" +#include "structures.h" + +#define DEBUG 0x0010 +#define PORT 9080 + +struct object **objects_map; +int objects_map_size = 0; +int objects_count = 0; + +struct connection **connections_map; +int connections_map_size = 0; +int connections_count = 0; + +void handle_player_input(struct object *obj, char *message) { + uint8_t action = message[0]; + + if (action & LEFT_MOVEMENT) + obj->x -= MOVEMENT_SPEED; + + if (action & RIGHT_MOVEMENT) + obj->x += MOVEMENT_SPEED; + + if (action & UP_MOVEMENT) + obj->y -= MOVEMENT_SPEED; + + if (action & DOWN_MOVEMENT) + obj->y += MOVEMENT_SPEED; + + obj->state = action; +} + +void handle_player_physics(struct object *obj) { +} + +int broadcast_event(int format, int object_id) { + struct object *obj = objects_map[object_id]; + + for (int iter = 0; iter < connections_map_size; iter++) { + struct connection *con = connections_map[iter]; + if (con == NULL) + continue; + + int message[] = { + format, + obj->id, + obj->x, + obj->y, + obj->colliding, + obj->force, + obj->state + }; + + SDLNet_TCP_Send(con->socket, message, sizeof(int)*CLIENT_MESSAGE_LEN); + } + + return 0; +} + +int handle_player(void *data) { + struct connection *connection_data = (struct connection *) data; + struct object *obj = objects_map[connection_data->obj_id]; + + SDLNet_SocketSet set = SDLNet_AllocSocketSet(1); + if (set == NULL) { + fprintf(stderr, "Error: cannot allocate memory for a new socket set\n"); + return -1; + } + + int ret = SDLNet_TCP_AddSocket(set, connection_data->socket); + if (ret == -1) { + fprintf(stderr, "Error: max socket count in socket set reached\n"); + return -1; + } + + broadcast_event(PLAYER_CONNECT_FORMAT, connection_data->obj_id); + + for (;;) { + char message[SERVER_MESSAGE_LEN]; + int ready_sockets = SDLNet_CheckSockets(set, 100); + if (ready_sockets == -1) { + fprintf(stderr, "Error: cannot call select() system call with SDLNet_CheckSockets()\n"); + continue; + } + + if (ready_sockets == 0) + goto update_client; + + int recv_len = SDLNet_TCP_Recv(connection_data->socket, message, SERVER_MESSAGE_LEN); + if (!recv_len) { + // player disconnected + fprintf(stderr, "Error: failed receiving message\n%s\n", SDLNet_GetError()); + break; + } + + if (DEBUG & GENERAL_DEBUG) + fprintf(stdout, "Notice: received '%s' and '%d' bytes\n", message, recv_len); + + handle_player_input(obj, message); + + update_client: + handle_player_physics(obj); + broadcast_event(OBJECT_PROPERTIES_FORMAT, connection_data->obj_id); + } + + // Communicate client disconnect + broadcast_event(PLAYER_DISCONNECT_FORMAT, connection_data->obj_id); + + SDLNet_FreeSocketSet(set); + SDLNet_TCP_Close(connection_data->socket); + + objects_map[connection_data->obj_id] = NULL; + objects_count--; + + connections_map[connection_data->id] = NULL; + connections_count--; + + free(obj); + free(data); + + return 0; +} + +void catch_alarm(int sig) { + fprintf(stdout, "Notice: force stopping server...\n"); + exit(EXIT_SUCCESS); +} + +int new_key_position(void **map, int slots) { + int slot = 0; + + if (slots <= 0) + return slot; + + if (map == NULL) + return slot; + + while (slot <= slots) { + if (map[slot] == NULL) + break; + + slot++; + } + + return slot; +} + +int main(int argc, char *argv[]) { + signal(SIGINT, catch_alarm); + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "Error: could not initialize SDL\n%s\n", SDL_GetError()); + return -1; + } + + if (SDLNet_Init() != 0) { + fprintf(stderr, "Error: could not initialize SDL net\n%s\n", SDL_GetError()); + return -1; + } + + IPaddress ip; + if (SDLNet_ResolveHost(&ip, NULL, PORT) == -1) { + fprintf(stderr, "Error: failed resolving host \n%s\n", SDL_GetError()); + return -1; + } + + TCPsocket server = SDLNet_TCP_Open(&ip); + if (!server) { + fprintf(stderr, "Error: failed opening socket at %d\n%s\n", PORT, SDL_GetError()); + return -1; + } + + for (;;) { + TCPsocket client = SDLNet_TCP_Accept(server); + if (!client) { + SDL_Delay(100); + continue; + } + + fprintf(stdout, "Notice: accepted a connection from client!\n"); + + int new_object_slot = map_allocate_value((void ***) &objects_map, &objects_map_size, &objects_count, sizeof(struct object)); + if (new_object_slot == MEMERR) { + fprintf(stderr, "MEMERR: failed allocating memory for new object\n"); + return STDERR; + } + + int new_connection_slot = map_allocate_value((void ***) &connections_map, &connections_map_size, &connections_count, sizeof(struct connection)); + if (new_connection_slot == MEMERR) { + fprintf(stderr, "MEMERR: failed allocating memory for new connection\n"); + return STDERR; + } + + struct object *new_object = objects_map[new_object_slot]; + struct connection *new_connection = connections_map[new_connection_slot]; + + new_object->id = new_object_slot; + new_connection->id = new_connection_slot; + new_connection->socket = client; + new_connection->obj_id = new_object_slot; + + if (DEBUG & PLAYER_DEBUG) + fprintf(stdout, "Created connection with id '%d' and object '%d'\n", new_connection->id, new_object->id); + + SDL_CreateThread(handle_player, "client", new_connection); + } + + return 0; +} diff --git a/04-collision/server/src/structs.h b/04-collision/server/src/structs.h new file mode 100644 index 0000000..aeb32d0 --- /dev/null +++ b/04-collision/server/src/structs.h @@ -0,0 +1,23 @@ +#ifndef STRUCTS_H +#define STRUCTS_H + +#include + +struct connection { + int id; + int obj_id; + TCPsocket socket; +}; + +struct object { + int id; + int x; + int y; + + int colliding; + int force; + + uint8_t state; +}; + +#endif diff --git a/04-collision/server/src/structures.c b/04-collision/server/src/structures.c new file mode 100644 index 0000000..60e0cf7 --- /dev/null +++ b/04-collision/server/src/structures.c @@ -0,0 +1,55 @@ +#include +#include +#include "structures.h" +#include "defs.h" + +int map_find_empty_slot(void **map, int slots) { + int slot = 0; + + if (slots <= 0) + return slot; + + if (map == NULL) + return slot; + + while (slot < slots) { + if (map[slot] == NULL) + break; + + slot++; + } + + return slot; +} + +// If there's Nx fewer used slots than total slots in the map, find the first possible one. +// Else reallocate the map and use the end slot +// Then allocate an object of size BYTES and add it into the map. +int map_allocate_value(void ***map, int *total_slots, int *used_slots, int bytes) { + int half_slots = (*total_slots) / 2; + int empty_slot = 0; + + if (*used_slots < half_slots) { + empty_slot = map_find_empty_slot(*map, *used_slots); + goto jump; + } + + empty_slot = *total_slots; + (*total_slots)++; + *map = realloc(*map, sizeof(void *)*(*total_slots)); + +jump: + if (*map == NULL) + return MEMERR; + + void *new_value = calloc(1, bytes); + if (new_value == NULL) + return MEMERR; + + (*used_slots)++; + (*map)[empty_slot] = new_value; + + return empty_slot; +} + + diff --git a/04-collision/server/src/structures.h b/04-collision/server/src/structures.h new file mode 100644 index 0000000..d216630 --- /dev/null +++ b/04-collision/server/src/structures.h @@ -0,0 +1,9 @@ +#ifndef STRUCTURES_H +#define STRUCTURES_H + +#include "structs.h" + +int map_find_empty_slot(void **map, int slots); +int map_allocate_value(void ***map, int *total_slots, int *used_slots, int bytes); + +#endif