Nantes Université

Skip to content
Extraits de code Groupes Projets
Valider 37d0bc18 rédigé par Nolan BLOURDE's avatar Nolan BLOURDE
Parcourir les fichiers

Ajout Tp Run

parent 297d2274
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
Affichage de
avec 1161 ajouts et 0 suppression
// Code generated by file2byteslice. DO NOT EDIT.
package assets
var RunnerImage = []byte("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\xe0\x00\x00\x01\x00\b\x06\x00\x00\x00\xaa\x1ez\xd7\x00\x00\x01\x84iCCPICC profile\x00\x00(\x91}\x91=H\xc3@\x1c\xc5_[\xa5\"U\a+\xa88d\xa8N-\x88\x8a8j\x15\x8aP!\xd4\n\xad:\x98\\\xfa\x05M\x1a\x92\x14\x17G\xc1\xb5\xe0\xe0\xc7b\xd5\xc1\xc5YW\aWA\x10\xfc\x00qssRt\x91\x12\xff\x97\x14Z\xc4xp\u070fw\xf7\x1ew\xef\x00\u007f\xbd\xccT\xb3c\x1cP5\xcbH%\xe2B&\xbb*\x04_\x11\xc0\x00z\x11Ő\xc4L}N\x14\x93\xf0\x1c_\xf7\xf0\xf1\xf5\xbc\xcf\xfd9z\x94\x9c\xc9\x00\x9f@<\xcbt\xc3\"\xde \x9e\u07b4t\xce\xfb\xc4aV\x94\x14\xe2s\xe2\xa8A\x17$~\xe4\xba\xec\xf2\x1b\xe7\x82\xc3~\x9e\x196ҩy\xe20\xb1Phc\xb9\x8dY\xd1P\x89\xa7\x88#\x8a\xaaQ\xbe?\xe3\xb2\xc2y\x8b\xb3Z\xae\xb2\xe6=\xf9\vC9me\x99\xeb4G\x90\xc0\"\x96 B\x80\x8c*J(\xc3B\x8cV\x8d\x14\x13)ڏ{\xf8\x87\x1d\xbfH.\x99\\%0r,\xa0\x02\x15\x92\xe3\a\xff\x83\xdfݚ\xf9\xc9\t7)\x14\a:_l\xfbc\x14\b\xee\x02\x8d\x9am\u007f\x1f\xdbv\xe3\x04\b<\x03WZ\xcb_\xa9\x033\x9f\xa4\xd7ZZ\xe4\b\xe8\xdb\x06.\xae[\x9a\xbc\a\\\xee\x00\x83O\xbadH\x8e\x14\xa0\xe9\xcf\xe7\x81\xf73\xfa\xa6,\xd0\u007f\vt\xaf\xb9\xbd5\xf7q\xfa\x00\xa4\xa9\xab\xe4\rpp\b\x8c\x15({\xdd\xe3\xdd]\xed\xbd\xfd{\xa6\xd9\xdf\x0fx\vr\xa9\xe6\x86ٛ\x00\x00\x00\x06bKGD\x00\xc0\x00p\x00\x00D\xa7;\xf4\x00\x00\x00\tpHYs\x00\x00\x12t\x00\x00\x12t\x01\xdef\x1fx\x00\x00\x00\atIME\a\xe5\x03\x15\x10\x0f\x02ߊ#\u007f\x00\x00 \x00IDATx\xda\xed]\xadw\x1b\xb9\x16\x97\xe6\x04\xf4O(kY\r\xcb\xd6\x0f\xd5E5\f\f{\x0e\xcbcYf\x18h\xb6a\x1b\x16/+\ftP\x1c\xb4)\vtY\xc2\xfa'\x94\xe9\x01\xfb:\xb2\xa5\xfb\xa1\xb1F\xe7\xe48\xe9\xdb\xe7߽W\xf7[\x9a\xb9Zm\xd6dn\xd4!k\xf2\xac\xf7\xfe\x0f\xf3\xcfF\x87\xfe\xfb\xf9D\xb7\xff\xee\x16\xb4\xb4\xc5?\x04\xf7\x10\x1a\xda\xe0\xc7bc\xd2Ж\x9eC\xf7\x1fc\xcd':\t\xf7P\xfe\xb1d\x10\x83\x1b\xa2\xa3\xc2b\xbc\xee߱W\xe9\xf8\xb9\xd0 \xb9\xa4\xf8\xc7ƭR\t\xf8r\xb6P\xa3\xeb\xf5\xa7\x84\xe0K\xc3υ\x86\x9c\x8c\x8f\x8b\u007f\n\xdc\n\x93\xc0C\xc30zJS8~.4\x94\xc8\u007f,n\xb4\x01\xdaVo\x9eֿ\xdf\x0e\xee\xd9ҟ\xd2\xf1s\xa0a\xf2\xac\x8d\xfbS\x02\xff\x98\xb8\x15\x16!\x8f\xdf\xc7\xec^\xa7t\xfc&\x1a(\x951\x97\x1aTj\x0f\xb0p\xa3\r\xd0\x06\x85\xdf9\x05P:~\x13\r`|\x14F\x98K\r*\xb5\a\x98\xb8'1\xb9\xaeo\x03\x94R\xea|\xf5\x8d<\xff.\x1d?\x17\x1a$k0)\xfe)p\xabXB\xde?O\xb6\u007f\x9f\xaf\xbei\u038d\xcf\x1d\xffvpo\xa8k\x91&\x1a8\xe4!Y\x83J\xe9\x006n\x94\x01\xde\x0e\xeeͧٙ\xfa9\xfd.\xd2|\xc8\x19\x9f\x93\x96:\x19\xc0\xdf\xd4J)Q\x83\xdd\x0e\xee\xcdt~n>\xcdΔ\x94ܱp\xab\x18\xe6\xed\xbf\u007fN\xbf\xab\x9f\xd3\xefj:?7\\\xc2w\x15\x90\vߍl6>g\xe4\xb1\xe5\xe0ʀ#\xfaJ\xd6`!\xde\\\x1d\xa0\x92\x81\xabg\xa9\xbaW\xa9~%m\xbc\xbd1\xb6\xf2S\x1ab[墢\xc1W\xe7}9[\xa8/g\x8b\x9d\xff\r\xdb\x19\xb4\xfd.\xaa\x06\x14\x85QW\xb1\x04\xd8^_jq\xd1P\xe7u9\x15_\"\xddN\xa9\x83\xa9\xf8\xf6\xc9\xd7N\v\xa5\xe4\x1e\xc3s\x95\xb3\xe2\xe7\xb8\xce\x9bvr\x19_\x9b\xbd\x00|j'\x00upH\xf98\xe4\xe0\xf2\xfeiv\xd6h\xa8ظ \x83ب\x1be\x80\xd4\xde&6\xad\xa2\xaa\u007f\\\xcf\x1e\xe2_\xda\xf88#\xb0\x0f\xdbW\x83a\xd1\x12\x8a\xae\\2p\xbf\xcfg\x841\xb8'\x87\x12\xb1-\xfe\x1fn\xf7\xff\x83\u007fx\x8d\xf1\xd3\xecL\xa9?\xbe\xbd\xfdÏ{R\xc3\xdfi\xc0\x9f_\xcfw\xe5\xf0\x0f\x8f\x03r\x1b0\\449\xb7O\xb33\xa5Vti\xb2O\xff~~=\xdfс\xf3\u007f\x94&\x95\xfd\xc37\x83\x89\x1b߄!T\xf6\xc6\xe5\x1a݆\x16\x9d\xef\x16\xe4ߦ\xe3\xd3\xecl\x87\x16\x8e.(\xe0\xfb\xa2\x0fu\n(}\xd1@\xb4\t#-\x80\xe9\xfc\xdcl\xd3\x1c\xdb\b\x85\x9c\x00DA1'\xf4Ƿ\xb5\x11xdAi\x84n\n\xe83D.G(\xba\a\x12\x06\x18̽\x89#\xc2t~n \xed\x80p\xbf\xa5\xe1\x8fol\x11\xc9U\xac\xad\x02\xfc\xb8\xe7\xedP\xfe\xb8\xdfO\x81\x1d\x19`\xd3SgT`\x88XYH\xe3ٚū\xbd\a\xe8\xce\xe6\xbf\xca\xec\xd0\xd2B\xee,\x06\xb8M}\xac\x14\x90\xbc\xe9\xf0\xf5|\xdb\xe9:\xffG\xe9m\xf1+\x91\x0eZ\xbcK5\xa5\xec=\xa0\xac}|F\xf8\xe9\xe1v\xab\x84\xf0\xbb\xb7/\x10\xa9\xf4uF\xb8u,\x9e=\xc0v:\xb6\xce\xd5\xe9\xbe\xc8\xe3H\x9c\xa9秇[5\x9bܒ\x9d3aE\x05\t\xba\xec\xa3\a\x8e\x12\xa1\xb6\x13\xbd1\x9eT\xa5\xdf*zCjM\xda\xf1ux\x11?\x88\x97\xac\t\xb9<|J=\x94\x03&\xc79X\xdb\xd40E\xe9\xc1ٶ\x91/\xd9y_Cm\x89\xe1|\x93\ue0ba\xady\xaaeG\xbec\xeb\x82a:@.gp;\xb87u\xb5PJ'\xf6\xe7\xd7\xf3\xed~\x03o\xa1C\xee\x90.`\xcaa6\xb9\xf5f\x15XzX\x1d\xa3\"JF\xbc\\\x1c\x04\x15\x1dM\xb5\x10\x95!4\x19\x9f\x94\xdcS\xa3`\xd55\x03pk\x1ei\x05\x87\xba\x8b\xab\xfe\n\xa5\x98R\xa9g\x13\xef\x87*\xa4\x9d\xed\x1c\xca\v\xcc\v\xd3\xf8\xab\x14\xc1K6B\xa4\xf1\xb9\x1e\xbfjK\v\x1cɤ(\xc3\xce9kd\x14\xe28|\xe7H=\xeb\x9c\rf\xdd}\xd0U\xb4\\:\x8f\xb9\xa4\x9f?\xa7ߕ\x9a\xf0]\b\xa8\xdbh8\v\x9b\xaa\xefƍ\"\xad\x9b\x1f\xff\xbd]\x9fg\xaa\xb4Ƈ\xafvK\xdd#p\xb8\xc5߄ɥ\ue44e\xbe1JN\xe5plZ>=\xdcFG\xe7\xb67JR\xa3,\x95\x9eI\x1bg\f\xfeIW\f.\x87\xf5s\xfa}\xe7l\x8a\xd3\b\x9b\x9e.8\xffGi5\xb9]_ƞܢ)Rʹ\x9b[&䔶\xe7\xb2N\xba\xce\x00\xa73\xd8^y\x9a\xf0\xdeC\x9d\xceύ\"\xbem\xb3m\xff\xafp\xe5\vFx;\xb87\xaa\xe3\xf76\xc5SP\xf7\xfd+\xdc\xef\x83\xf1ѲwW\x8f\xd2\xd87\x97\x01D<\xf9\xe6\xe2uW\xa3\b4\x89\xa8\xb3\x06\t\xbdH\xc1<\xc8\x00\xed\xbb\u007f\xf6\xe5\x03#\"q\xe2ÝGN~\xb1j<\xd1\xd4\xfd\xe1\x96\xed6\x93\x94^\xc4`n\x05r蜶\xf9d\xff\x15\xe4\x939\xdf|\xc0\xb6\xf8T\xf3\x01\xdb\xe0S\xcf\a\xc4܃.\xce\a<\x94\u007f,\x19\xc4\xe0\x86\xe88\xc1b\x1c\xfe\xfdPBz\xfc<hHu\x16\x02\xc6+\xb2\aظ'\xa9\x04,/\x95\x1a]\xbf}r\v\xfe\x98\xf1k\x8c\xa2\x96\x86.\x19S$\xad\"\xfcS\xec\xfd\t\xb2\a\xd4\x18ߓ\xf0\x1d\x1aK\xf8\x91ߣ3P\xfe\xd2/\xab\x8b\xf0\x1f\x1bu\xa3\x0f\xfb\xbf//\x15[!Q:~&4\x18\xcf\xcf\xd1\xf3\xefÍ\x1d\xcbv\x82E\b\x84b\xa9\r(\x11\xbf\x89\x86\xe5\xa52\xa3k\xb2\x88`j\xfe]wq\x0f\x0e\xc9^|\xb8\\\xd9\xcf\xd6\xdb-/\xd7?\xcc\x1e\xb0t\xfcV4\xc0\xbf\x13E\x04\xe3\xd2`\u007f\x1e\xf1\x1e\xa0\xe3Ƥ\xa0\xda\xe7\r6\x1e\x81\xc3\x05\x94\x8e\x9f\v\r\x925\x98\x14\xff踱5\xa0\xbe\xf9\xb5\x13\x825a\xaa\xd39|\xc2\xc8Ӛ\x06\x0ey\bנR:\x80\x8a[E\n\xde\ffJ\xad\xa6b͇l\xf19i\xa9\x93\x01\xfcM\xad\x94n-\xc4%\xff\x9b_\xeb=\x90\x92;\x16\xae\x8ea\x1e~\x87͇u\xf1\x9e\xc5\xeb\xee0l\xd3@\x8d\xef\x136\xe0\xdb\xd8\x1c\xca\x1f\xda\a[9\bi0\xae\x01:\xc6G\xce\xf7j\xba˫\xbb\x0fT\r(\xd7\xf0}\xfbϑ\x82\x16\xb7\x9a<\xddͯݦ\a\x97\xf1\xd5-B\x1aZ\xd5Bةx\xdb\xef\x82\xff\x0e;*RD\xd9*\x96\x007\xfaI,.\x1aB\x82\x0f\xe1\xe7`|9\xd4\xc1T|\xfb\xe4\xebF~\t\xb9\xc7\xf0\\\xe5\xac\xf89\xaeѵҫ\xa9\x8c\xf1\xb5\xd9\v\xc0\xa7v\x02P\a\x87\x94\x8fC\x0e.\xef\x83Y\xb3\xa1b\xe3\x82\fb\xa3n\x94\x01R{\x9bش\x8a\xaa\xfb\xe8z\xf6\x10\xff\xd2\xc6\xc7\x19\x81}\xd87\xbf\xf6\x9bQX\xb4\x84\xa2+\x97\f\xdc\xef\v\xd5݇\xe2\x9e\x1cJ\x040?\xbf\x92\x8fF\x83\x99R\xd3\xdfo\u007f\xcf\xde\xd1\x1a\xbe\xbd\xf1\xf3+\xa5&W\xfcr\xb0\xe9\x80.(\x17\rM\xce͍\x06\x94\xbc\x03ϓ\xab]\x1dx\xba\xa1s>\xa3k\xa5\x87\xbf\xd7\xf8X\xb8\xd1M\x18JeoZ\xae\xd1\x01-\xd4gA\xf6wK\xf2o\xd31\x98\xed\xd2\xc2q\x06\t\xf8\xbe\xe8C\x9d\x02rg\x19ԫ\xea\x92\x00n~\xad\xcf\u007f\\#\x94r\x02\x10\x05\xa5\xf0\xa7\xbf\xd7F\xe0\x93\x05\xa5\x11\xba)\xa0\xcf\x10\xb9\x1c\xa1\xe4\x1e\x88\x18`(\xf7\xa6\x8e\b7\xbf\x94\x81\xb4\x03\xc2=\xd00\xfd\xcd\x17\x91\\\xc5\x02\x05\x98\xbd\xe3\xedP\xce\xde\xed\xa7\xc0\xae\f\xb0\xe9\xa93*0D\xac,Į'\x9b20{\x0f\xb0\xd7\xf0\xe2\xcd\xfb!+JP\xa1\x8d\xa5\xf4z \x04\xa8\x8c3:\xd7)\xb6\x14\r>yPѳ\xbcT\x06\xa2\xae\xab\x8c\x185\xd8\xf0B\x19\x88j\xbe\x03\xee&ǂɳ}\xf0\x1e\x92q\nn'\x0e\xe2!\xfa\xf9n\x9bH/W\xe8\x12t\xd9G\x0f\x1c\x0e\xa0\x0ecx\x91\xce?\x18\x9f\xaf\xdb\xcc\xe9t]^\xc4\x0f\xe2%=>ew\x8b*\x1aJ`r\x9c\x83\xb5M\rS\x94\u07beR&\xa5\x83M\xb5%\x86\xf3\xadR6\xbf)$c-;\xf2\x1d[\x17\f\xd3\x01r9\x83\xe5\xa52u\xb5PJ'vbe:\xc0[\xe8\x90;\xa4\v\x98r\xb8x\xef\xcf*\xb0\xf4\xb0:FE\x94\x8cx\xb98\b*:l\xfe\xe1\b\xc4>\n\xa22\x84&㓒{j\x14\xac\xbaf\x00n\xcd#\xad\xe0Pwq\xd5_\xa1\x14S*\xf5l\xe2\xfdP\x85t\x9b.\x87\xf0\x82\xcdw(\xf3\xc24\xfe*E\xf0\x92\x8d\x10i\xfc\xa6697-p$\x93\xa2\f\xf69kl\x14\xe28|\xe7H=\xeb\x9c\rf\xdd}\xd0U\xb4\x8cn\xe2g\x91~\xae\xa6J\xa9k>>\xeb6\x1a\xce\xc2n\xa6\xca\xc4<\x9b6\xbcXw\x9a۞g\xd6\xc9\xdcW\xbb\xa5\xee\x118\xdc\xe2o\xc2\xe4R\xf7HG_\x8e\x87\x8f\xdb\xcaۦe~\x15\x1f\x9d\xdb\xde(I\x8d\xb2Tz&m\x9cl瀥v\"%\xde\x00\xe0:\x9c\x90\xec\x9fn\xd6N\x01>\xa9\x9a,\xa7\xb4\xfd\xa8\"`\t\xe9'D\x17\x89g!9\x14\x17\xda\xff\xa1\xba.\xb6\xbe\xb3\xd3\xd1\x1c\x9e\xa0\xe9\xb4\x01\xc2}?P\b\xf7o\xeeh\xb4\x9a\xee\xdfգ\\p\x19@\x82_\xb8x\xdd\xd5(\x92\xfa\ue51c\xf5\x82\r\x13\x00B\x9f\xdcQA\x02\u007fx!\xc3/5\xaf\x94\xfc\x00\xddǪ\x17)\x98\xd1ވ\xeb\xe2o\x8f\x9f7\r\x92K\x8a\u007fL\\ԧ!$\x05P\x12~.4\xe4f|\x1c\xfc\x8b?\r\xe1\x12\xf0\xe5l\xa1F\xd7\xebO\t\xc1\x97\x86\x9f\v\r9\x19\x1f\x17\xff\x14\xb8\xa8]\xd0.\x9e\xc3\x1c\x13~i)hN\xfc\xc7\xe2F\x1b\xa0m\xf5\xe6i\x01\x9fl\x8d\x89\xd2\xf1s\xa0\xc1\x9e\xc0\xc44\x0f#\v\xfe1q+,B\x1e\xbf\x8fٽN\xe9\xf8M4P*c\xc8\xd8$\x8d\x90s\x0f\xb0p\xa3\r\xd0\x06\x85\xdf9\x05P:~\x13\r`|\x14F\x98K\r*\xb5\a\x98\xb8I]PW\xe0z8f\xed\x00\x96\x8a߆\x06\xdb\xf0\xb0\xe9\xf2\x19\xa0\x1e\x8e\x95yZ\x1c\xfd\x1e`\xe3F\x13k\xe6ʨ\xc1\x82]\xf1\xba\x80\x0f\xcaOMW\x93\f\xa8\xf0\xf1\xfbX\xcc\x11I\xe9\x00&nT\nj\x9e\x16k\x02Vc\x91\xe6C\xce\xf8\xac\xb4\xd4Ȁ\xcb\tH\xd4`\xe6ial#\x10\x91;\x12\xae\x8eR>X\xab]\x81\xeb\t}\vx\x8fa\x8b\x06j|\xaf\xb07\xf866\x87\xf2\a\xf7a@\x1f\x8d\xec\x14\x14\f\xd06>ʣ\x80\xf1.\xaf\xce>\x98\xa7\x85\xa1\xe0\xdd5|\xdf\xfe\xb34aJ[M\x9e\xce̕1O\v\xc3n|uޕ\x88\x06\x9f\x81}9[@3f\xc7\x11aF\xa6\xb6\xdf\xa2\x88\xb2U4\x01\xab\xb1\xbcU\xacƲ\xc6\x17\xc0\xcf\xc1\xf8\xa8\xd7\xe8Z\xe9/\x9fwy\xb6\xf9\xa6T~\xaf|\a\v\x19\x1dHܛ*g\xc5\xcfq\xe9\xe1X\xab\xd5X\xc4\xf8\xda\xec\x05\xe0S;\x81m\x1d\x1cP>\x169\xb8{0X\x90v\u007f\xebR\xfeب\x1bg\x80\x83\x85\xac\x014)\x06!\xa6\xabx9\x19\x1fk\x04\xf6`\x9b\xb92Tup0\xba2\xc9`\xef\xfb\x02u\xf7\xa1\xb8'\x87\x12\x01\xcc\u007f\x9d\x1b\x8c\x9eN\xda\x1a,\xd4\xd7\xe9\xb7\xed\x9f\x0f\xb3{R÷7\xfe\xebܨ\x87\x89v\xe4\xa0Y\x1cЖ\x8eM\x17\x94\x8b\x86F\xe76X(\xa5\xc6di\xb2O\xff\x1e&zG\a\x96K\xad)e?\x1a\x19\x83\x89\x1b݄\xa1T\xf6\xa6\xe5\x1a\x1d\xd0\xe2\xd6!\x94\x91P\x92\xff\x1d\x1e\a\x8b\x1dZ\xa8\xb2\x00/\xbe/\n\x12\xa7\x80\x12g\xbe\x94\xab\xea\x92\x00\xcc\\\x19Hsl#\x94r\x02\x10\x05\xc5\xf0\xa7\xdf\xd6\x11\xd0#\vJ#\xdcK\x01=\x86\xc8\xe5\b%\xf7@\xc4\x00C\xb97uD0se \xed؆\xfb\r\r_\xa7\xdf\xd8\"\x92\xabX\xa0\x00\x0f\xb3{\xd6\x0e\xe5\xc3\xec~/\x05ve\x80MO\xadQm\f\x11+\v\xb1\xebɦ\f\xcc\xde\x03\xec5\x1a\x19c\xd3\xd2F\xee\a\xc9\x14\xb3\x16 -\xfeA\b\xd6\x15 \x91\xce[\x83bK\xd1\xe0\x93\a\x15=\xe6ia \xea\xbaʈQ\x83\x8dF\xc6@T\xf3\x1dp79\x16L\x9e\xddkg\xd8\x83x\x88~\xbe\xdb&\xd2+t\x0fT\x82\x06\xea\x1a\xb8\x8d\xa2A\x93\")\xbaCJ\xe9\xe96s:]\x97\x17\xf1\x83xI\x8fO\xd9ݢ\x8a\x86\x12\x98,\xe7`-S\xc3\x14\xa5\xb7\xaf\x94I\xe9`Sm\x89\xe1|\xab\x94\xcdo\n\xc9h\x1e\u05ca|\xc7\xd6\x05\xc3t\x80\\\xce\xc0<-L]-\x94҉}\x98\xe8\xed~\xbb\x8fV\xb5\x8d~\x98r\xd0\x13\xe5\xcd*\xb0\xf4\xb0:FE\x94\x8cx\xb98\b*:v\xf8\xdf\x1c\x81\xd8GAT\x86\xd0d|RrO\x8d\x82U\xd7\f\xc0\xady\xa4\x15\x1c\xea.\xae\xfa+\x94bJ\xa5\x9eM\xbc\x1f\xaa\x90n\xd3\xe5\x10^\xd0o\xbf\x042/L\xe3\xafR\x04/\xd9\b\x91\xc6oj\x93\xb3\xd3\x02\x8f\xc5$(\x83}\xce\x1a\x1b\x858\x0e\xdf9R\xcf:g\x83Yw\x1ft\x15-\x97\xcec6\xe9'\xf3\xa5\xf4\xba\x8d\x86\xb303W&\xe6ٴ\xd1Ș\xafs\xb59\xcfTI\x8d\x0f_햺G\xe0p\x8b\xbf\t\x93K\xdd#\x1d}9\x1e>n\x1d\x11,Z\xbe\xceMttn{\xa3$5\xcaR陴q\xc6\xe0\x9ft\xc5\xe0\xb2X\xceS\u061cF\xd8\xf4t\xc1r\xa9\xb5^*\xa5\x94V\xebO\x1cEJ9ws˄\x9c\xd2\xf6\\\xd6I\xd7\x19\xe0t\x06R\xf7\x0e9\x14\xf7\xad\xfd?F\x95/\x18\xe9\xf2\xbdM\xf9\x14ts\xdfo\xab\x10\xee\xdf\xdc\xd1h5\u07bb\xabG\xb9\xe02\x80\x04\xbfp\xf1\xba\xb3Q$\xf1\xdd)9\xeb\x05\x1b&\x00\x84>\xb9\xa3\x82\x04\xfehdD\xf8\xa5\x1f\xa0\xfbX\xf5\"\x05\x13\xb5\x98f\xad\x89\n\xc7υ\x06\xc9%\xc5?&\xae\xc6\"@Z\x00%\xe1\xe7BCn\xc6\xc7\xc1?6n\x95L\xc0`\xa1\xf4D\xb1\xbd'\xa6t\xfc\\h\xc8\xca\xf8\x98\xf8\xa7\xc0E\xbd\v*\xedyK\xc7/-\x05͉\u007f\xfe\x17\xf3\x0e\x84\xe7\x9f\x01\rpu\xcd\xfe)\x82\xffA&\xf3\x01}\xaf\xe8\x96ڀ\"\xf1\x1bh }/L\xc0\xd8$\x8d\x90u\x0f\x90pq\xde\t\x03\xbfs\n\xa0t\xfc\x06\x1a(\xe7\x03fS\x83J\xed\x01\"\xee\xe1oE\xf3\x05l\x02\xfd\xeb\x10\n\xc7υ\x06\xc9\x1aL\x8a\u007f\n\xdc*\x96\x90\xd7;\xebo\xa6gẂ\xcf\xf2n\xce\x06\x1aX\xe4!X\x83J\xe9\x006n\xf4|\xc0\x0fӅz\x9d\xc9\xcd\xe7\xcb\x15\x9f\x93\x96:\x19p\xcd\a\x94\xa8\xc1\xcc\xd3¼\x9c*\xf3a*3\x1f\x10\x137i> l>\xac\x8fw\xfc\xf3\x01m\x1a\xa8\xf1}\xc2\x06|\x1b\x9b{D\x99-\x03[9\xa8\x01\xc1\x00\x99\xe64\x02߯\xb3\xf1\x0e\xaf\xee>P=;\xe8\x1a\xbeo\xffy\x9a0\x85\xad&O\xf7rZ\xce|\xc0\xb6\xb5P?\x1f\x10\xd9\x00\xc4\xe2\xa2!$\xf8\x10~\t\xf3\x01\xdb\xd4\xc1T|\xfb\xe4\xebF~\t\xb9\xb3\xcd\a\xcc\xc1\xf8\xc4\x14o8֯\xb3\xb1\x88\xf1\xb5\xd9\v\xce\xf9\x80u\xb5\x10\x87\x1c\\\xde?Ly\xe6\x03\xfaR~\xd6\xf9\x80\xd4\xde&6\xad\xe2\x9a\x0f\x18\xe2_\xda\xf88#\xb0\x0f\xfb\xe5\x94\u007f> \x97\f\xdc\xef\v\xd5݇\xe2F7a\xf4p\x8c\xf2}\b\xe9\x88\xfd\xef\xa4\n\xe8⾜*\xf5\xf1N\x96\xff\xd7٘\x8d\x06\x17\xd7\xe7\x88\xdc\xd7P`\uf16b\u007f\x02{`0q\xab\x04AHF\xc1\x1d:l\xc3\xe3\x1a\x8b%̿v\xd2.\xf2,\xc0\x87\xef\x8b>\xfd|@b\x03\x94\x14\xc0˩2\x90\xe6\x04\"0\xab\x13\xf8x\xb7\xf6\x84R\xf8z\xb8\xaeE}\xb2\xe0\x9a\x0f\x18\xaa\x87\xb9\x1c\xa1\xe4\x1e\x88-8\b݄c\xa3\x94\"\xf7\xba\x80g\xd5\x19\x06Z\xff\x1c\xf8\x1e\x05\xdc\xfe\xb8G\x10\x92{\xe0\xfeP5`\xc0\x19\xba?\xc8\xfb}\xf0\x1eP\x88ڭm1u\xbf3\xf3\x01A\bPw\xf4\xf3\x01e\xe7\x03\x12\xd7`\x06\xa2\x9a\uf01bs>\x87}\xf0^\xec|\xc0\x97Se>\xde\xf9o\x9bH\xaf~>\xa0\xbfI\x91\xb8\xdf\xcav\xb6M\x8e\x87\xba\xe1B\xb9\xb7\x9d\x99\x0f\xa8\x94\xea\xe7\x03\xb6\xc0\x94\x9e\x0f\x88М2v䓜\x0f\xd8T[\x163\x1fЎ|\xfd|\xc0\xb0\x12p\xce\a4O\x8b\xbd\xf4\xd3~\"\"\x96\x96\xcdwf3\x1f\xf0\xe3]?\x1f0\xabH\xdc\xcf\a|\xe3\x1f\x8e@\xec\xa3 *C\xe8\xe7\x03\xf6\xf3\x01\x1b\xa2_\x97\xe7\x03\xbaM\x17\xc9\xf9\x80\xa1̫\x9f\x0f\x98\x01>f\xcb\x1d\x83\x168\x8bKQ\x86\xa6\xa3\x84~>`?\x1f0\xab\xf4\x93\xfbRz\xddF\u007f\xbcS\xeaE\x8d\xa1c\x1c#\x0f\xf3\xf1ns\xb3h\xda\xcf\a\xe4Z\xfd|\xc0H~9\x1e>n+o\x9b\x96\xcd͐\xe8\x06\b\xf5~\xa7d\x0e\xc78\x1f\xb0\xea\x8a\xc1\xe5\xb0$\xde\x00\xe0:\x9c\x1aكSHv\x0euu]JM\x96\xcb\xea\xe7\x03\x1eV\x94\xab\x17\xc5\u007f\a\x95Cq\xb7\xed\xff;\xba\xf9\x80\xf0\x04I\xbf\x12\x8btP\b\x8a;\x80\x11\r\x03V|\t~m>\xa9\xb0)yr\xeeO\x1e\x9d^pb\xba@\"F\xe0*%3\xbe\b\xbfԼR\xf2\xc3e|Rz\x91\x82\xa9\x13\x15\x11\xf3\xfbz\xfcn\xd2 \xb9\xa4\xf8G\xc3Ո\x04H\v\xa0$\xfc\\h\xc8\xcd\xf88\xf8GŭR\tX^\xee~r\v\xbe@\xfc\\h\xc8\xc6\xf8\x18\xf9G\xc7ž\v*\xedyK\xc7/-\x05͉\u007f\xdew\xc2\xd8Voy\x02\xb6\xc6D\xe9\xf8\x99\xd0`<?G\xcf?&n\x85E\xc8\xe8\x9a\xdf\xdfD\x03\xb12\x9a\x88\x1a\xe9h\xf6@r\xef\xb7\xdeny\xb9\xfea\xf6\x80\xa5\x01\xfe\x9d\xc8\b\x8dK\x83\xfdy\xc4{\x80\x8e\x1b\x13\x01\xb5\xcf\x1bl<\x02G\xfe]:~.4H\xd6`R\xfc\xa3\xe3\xc6^E\xd3Ë7\x8b\x1f]\xb3oz\xd6\xf8\x10u\x88骥at\xad4u=\xb4\xbc|K\xbf\xecZ\x88i?\xa4t\x00\x15\xb7\x8a\x14\xbc\x99\xbdSj\xfa[\xac\xf9\x90->'-u2`r\x02\"\xb5\xd0\xf2R\x99\xe1\xc5z\x0f\xa4\xabc\x98\x87\xdfa\xf3a=\xdd\xd0{!\x97a\x9b\x06j|\x9f\xb0\x01\xdf\xc6\xe6P\xfe\xd0>\xd8\xcaAH\x83q\r\xd01>r\xbe\xa7\xbfwyu\xf7\x81*\x12\xbb\x86\xef\xdb\u007f\xf2\bX\xe2j\xf2tËݦ\a\x97\xf1\xd5-B\x1aZ\xd5B\xd8M\xa0\xb6\xdf\x05\xff\x1dvT\xa4\x88\xb2U,\x01n\xf4\x93X\\4\x84\x04\x1f\xc2\xcf\xc1\xf8\x98j\xa1\x1d\x9em\xbe)\x95\xdf'_7\xf2K\xc8=\x86\xe7*g\xc5\xcfq\x8d\xae\x95\x9e\xfe\x961\xbe6{\x01\xf8\xd4N\x00\xea\xe0\x90\xf2q\xc8\xc1\xe5}\xf6\xae\xd9P\xb1qA\x06\xb1Q7\xca\x00\xa9\xbdMlZEu\xee\xe5z\xf6\x10\xff\xd2\xc6\xc7\x19\x81}\xd8vw\x10;\x15\x0fEW.\x19\xb8\xdf\x17\xaa\xbb\x0fōn\xc2|\xf9k_ϵ\xd6,\nho\x80M\xc7㟚T\x01]\xdc\xff\xfcO\xab\u007f\xff6\xa2\xfcO\u007f+6\x1a\\\\\x9f#r\x8f?\xb0\xf7\xc2\xd5?\xee=0\xc6\x18L\xdc\xe8&\f(\xbb\xc4r\x8d\xce6<J\xefo\u007f\xb7$\xff6\x1d\xb3w\xbb\xb4\x10\xde~\xd9\xc3\xf7E\x1f\xea\x14P\xe0̗tU]\x12\xc0\xf0b}\xfe\x13\x8a\xc0\xdcN\xe0߿\xd7QP\n\xff\xcb_FM\u007f\xfbeAi\x84n$\xf4\x19\"\x97#\x94\xdc\x03\x11\x03\f\xe5\xde\xd4\x11ax\xa1\f\x84|\b\xf7\xbf\f[Dr\x15\v\x14\xe0\xf1O\xcdڡ|\xfcS\xef\xa5A\xae\f\xb0\xe9\xa93*0D\xac,Į'\x9b20{\x0f(RN\x9b\x966r'\xad\x01\xeb6\x96\xd2\xeb\x81\x10\xa0\xee\xf0]\xb5\xe2\x8a\xceu\x8a-EC\xe8\xea\x19\x05=\xcbKe(k0c\x8c\x81\xa8\xe6;\xe0nr,\x98<\xdb\a\xefM\xd7\xfbbp;q\x10\x0f\xd1\xcfw\xdbDz\x85\xee\x81J\xd0@]\x03\xb7Q4hR\xa4,0>_\x93\x87\xd3\x88\x1f\xc4Kz|\xae\x0e#f4\x94\xc0\xe48\ak\x9b\x1a\xa6(\xbd}\xa5LJ\a\x9bjK\f\xe7[\xa5l>Ǎ{7\r9\xb6.\x18\xa6\x03\xe4r\x06\xcbKe\xeaj\xa1\x94N\xec\u007f\xfe\xa7\xb7\xfb\r\xbc\x85\x0e\xb9C\xba\x80)\x87\xa7\x1b\u007fV\x81\xa5\x87\xd51*\xa2d\xc4\xcb\xc5Ap\x9c\x85\xc2\x11\x88}\x14De\bM\xc6'%\xf7\xd4(Xu\xcd\x00ܚGZ\xc1\xa1\xee⪿B)\xa6T\xea\xd9\xc4\xfb\xa1\n\xe96]\x0e\xe1\x05\x9b\xefP\xe6\x85i\xfcU\x8a\xe0%\x1b!\xd2\xf8MmrnZ\xe0H&E\x19\xecs\xd6\xd8(\xc4q\xf8Αz\xd69\x1b̺\xfb\xa0'\xe23\xba\x89\x9fE\xfa\xc9})\xbdn\xa3\xe1,lx\xa1L̳i\xd0\xfcx\xfcS+\x95\xd8\xf8\xf0\xd5n\xa9{\x04\x0e\xb7\xf8\x9b0\xb9\xd4=\xd2ї\xe3\xe1\xe3\xb6\xf2\xb6i\xf9\xf7o\x13\x1d\x9d\xdb\xde(I\x8d\xb2Tz&m\x9c1\xf8']1\xb8\x1c\x96{\x01\x99\xd3\b\x9b\x9e.\x80c\x1a}\xa3Q\x15)\xe5\xdc\xcd-\x13rJ\xdbsY\x9d\x9f\x0f\xc8\xe9\f\xa4\xee\x1dr(.\xb4\xffG\xc8\xf2\x05#\x84\xdb3]\xbe\xb7)\x9e\x82\xc2}?P\b\xf7o\xeeh4\xfd\xbd\u007fW\x8frA\x94\x91\xe0\x17.^w5\x8a\xa4\xbe;%g\xbd`\xc3\x04\x80\xd0'wT\x90\xc07\x9b\xc5\xcd/5\xaf\x94\xfc\x00\xddǪ\x17)\x98\xd1\xde\xc8\xcc\xf7A\xf4\x84qfz\xe1\xf8\xb9\xd0 \xb9\xa4\xf8\xc7\xc4\xd5X\x04H\v\xa0$\xfc\\h\xc8\xcd\xf88\xf8\xc7ƭR\tx|VJO֟\x12\x82/\r?\x17\x1ar2>.\xfe)pQ\xef\x82J{\xde\xd2\xf1KKAs\xe2?\x167\xfe\x9d0\x96\xd5K̦+\x1d?\a\x1a\xcc\\\x19\xf7\xa7\x04\xfe1q+,B\xbe|\xe6\xf7:\xa5\xe37\xd1@\xa9\x8c!c\x934B\xce=\xc0\u008d6@\x1b\x14~\xe7\x14@\xe9\xf8M4P\xbd\x9e=\xa7\x1aTj\x0f0q\x0f6@_\xae\xfb\xf8\xbc\xdd\x04\xf2\xfc\xbbt\xfc\\h\x90\xac\xc1\xa4\xf8\xa7\xc0\x8d&v5\x9a\x98_\x9f\xe7J)\x99\xbb\xa19\xe3s\x8d\x06k\x92\x01\x15\x1d\x10\x01\x1f\x9fמߍ|\\\xfb!\xa5\x03\x98\xb8\xd1\xf3\x01\u007f}\x9e\xab\xf7\xcf\x13\x91\xe6C\xce\xf8\x9c\xb4\xd4ɀ\xcb\tH\xd4`\xcbKel#\x90\x90;\x16n\xd2|@\xd8|X\x83\xe5\x9c}>\xa0M\x035\xbeO\u0600ocs\x8f(\xb3e`+\a\x15\rv\r\b\x06h\x1b\x1fe\x1a\b|\xbf\u007f\x9e\xec\xf0\xea\xee\x03ճ\x83\xae\xe1\xfb\xf6\x9f\xa5\tS\xdaj\xf2t\xabѤ\x98\xf9\x80mk\xa1~> \xb2\x01\xd6E?\x89\xc5ECH\xf0!\xfc\x12\xe6\x03\xea\x89\xd2?\xe7\x93\x1d\x9e%\xe7\x03\xba\x91_B\xeel\xf3\x01s0>\xa95\xbaV\xfa\xfd\xf3D\xc4\xf8\xda\xec\x05\xe7|\xc0\xbaZ\x88C\x0e.\xef\xbf>\xcfY^N\xe5K\xf9Y\xe7\x03R{\x9bش\x8ak>`\x88\u007fi\xe3\xe3\x8c\xc0>\xec\xd5h\xc2>\x1f\x90K\x06\xee\xf7\x85\xean\xb6\xf9\x80\x89<K\xfe\xf9x6\x1dW\x9f\xf9\xe6\x03^=\x1bu\xa3\xceՅ\xba\x15\xe5\xff\xfd\xf3\x84\x8d\x06\x17\xd7\xe6\x03¾s\xef\xc1h\xb4~\xae\x11\v7\xba\t\x03\xca.\xb1\\\xa3\xb3\r\x8fk,\x96$\xff6\x1d\xbf>\xcfwh\xe1\x9c\x0f\xe8\x8b>\xfd|@b\x03\x94\x14\xc0j41\x90\xe6\xf8\"0\xb7\x13\xb8P\xb7\xeaF\x9d\x8b\xe1_=\x1b\xf5\xfey\xe2\xcfF\x98\xe6\x03\x86\xeaa.G(\xb9\a\"\x06\x18ʽ\xa9#\xc2j41\x10\xf2!\xdc\x03\rWφ-\"\xb9\x8a\x05\np\xf5\x99w>\xe0\xd5g\xbd\x97\x06\xb92\xe0\x9c\x0f\b\x86\x88\x95\x85\xd8\xf5dS\x06f\xef\x01E\xcai\xd3\xd2F\xee\xa45`\xdd\xc6Rz=\x10\x82}\x05\xa8\x9f\x0f\x18\xae\xbd(\xe9Y^*CY\x83\x8dF\xc6@T\xf3\x1dps\xce\xe7p\xaf\x9d\x159\x1f\x10\xa2\x9f\xf4\xea\xe7\x03\xfa\x9b\x14)\v\x8c\xcf\xd7\xe4\xe1t\xba./E\xcf\a\xe4\xea0bFC\tL\xe9\xf9\x80\xa9i (\xbd}\xa5LJ\a\x9bj\xcbb\xe6\x03ڑ\xaf\x9f\x0f\x18V\x02\xce\xf9\x80u\xb5PJ'\xf6F\x9do\xf7;\x87\xf9\x80\x83\x0f\x98U$\xee\xe7\x03n\xf9\x87#\x10\xfb(\x88\xca\x10\xfa\xf9\x80\x99\x18\x80[\xf3H+x?\x1f\x10w>\xa0\xdbt\x91\x9c\x0f\x18ʼ\xfa\xf9\x80\x19\xe07\xb5ɹi\x81#\x99\x14e\xb0\xcfYc\xa3P?\x1f\xb0\x9f\x0fȖ~\xae\x95~.\xd2|\xf1\x9eG>+\xb5\x1a)\x13\xf3l\xdahd\xccŦ\x8e[~\xee\xe7\x03r\xad~>`$\xbf\x1c\x0f\x1f\xb7\x95\xb7\xba\x8d\x8e\xcemo\x94\xa4FY*=\xeb\xe7\x03\x1e\xf9r/ s\x1aa\xd3\xd3\x05˥\xd6\x03\xa5\x94Rs5@T$\xcc\xf9\x809\xa5\xed\xb9\xac~>\xe0\x01\v\xd2\x13\x14\xf7\x171\xe6\xb6\xfd\xbfĕ\xaf=\x1f\xf0\xe2\xb9\xdb\xf76\xc5SP\xb8\xef\a\x9e\xcc\xfd\x9b;\x1a\xbd\u007f\x9e\xec\xdd\xd5#\x8dB\x9b\xcb\x00\x12\xfc\xc2\xc5\xeb\xaeF\x91\xd4w\xa7\xe4\xac\x17)\x98\a\x19 \x1c\xbaB\x9d\xe1\xfeͽ\xa9p\t\x97\x13\xff\xf3\x8bU\xe3I\x1a߅\xbae\xbb\xcd$\xa5\x171\x98\xd1\x02\xe1\xba\xf8\xdb\xe3\xe7M\x83\xe4\x92\xe2\x1f\x13\x17\xf5i\bI\x01\x94\x84\x9f\v\r\xb9\x19\x1f\a\xff\xe2OC\xb8\x04|9[\xa8\xd1\xf5\xfaSB\xf0\xa5\xe1\xe7BCN\xc6\xc7\xc5?\x05.\xea]\xd0.\x9e\xc3\x1c\x13~i)hN\xfc\xc7\xe2\xc6OG\xb2\xac\xde<-\xe0\x93\xad9P:~\x0e4\xc0\xf1\x82\xfdS\x02\xff\x98\xb8\x15\x16!\x8f\xdf\xc7\xec^\xa7t\xfc&\x1a(\x951dl\x92\aX\xb8\xf1\x13r-P\xf8\x9dS\x00\xa5\xe37\xd1\x00\xc6Ga\x84\xb9ԠR{\x80\x89\x9b\xd4\x05u\x05\xae\x87c\xd6\x0e`\xa9\xf8mh\xb0\r\x0f\x9b.\x9f\x01\xea\xe1X\x99\xa7\xc5\xd1\xef\x016n4\xb1f\xae\x8c\x1a,\xd8\x15\xaf\v\xf8\xa0\xfc\xd4t5ɀ\x8a\x0e[\t\x1f\xbf\x8f\xc5\x1c\x91\x94\x0e`\xe2F\xa5\xa0\xe6i\xb1&`5\x16i>\xe4\x8c\xcfJK\x8d\f\xb8\x9c\x80D\rf\x9e\x16\xc66\x02\x11\xb9#\xe1\xea(僵\xda\x158\xc7x\xe2=\x86-\x1a\xa8\xf1\xbd\xc2\xde\xe0\xdb\xd8\x1c\xca\x1f܇\x01}4\xb2SP0@\xdb\xf8(\x8f\x02\xb6|\xafƻ\xbc:\xfb`\x9e\x16\x86\x82w\xd7\xf0}\xfb\xcf҄)m5y:3W\xc6<-\f\xbb\xf1\xd5yW\"\x1a|\x06\xf6\xe5l\x01͘\x1dG\x84\x19\x99\xda~\x17U\x03\x8a\"\xcaV\xd1\x04\xac\xc6\xf2V\xb1\x1a\xcb\x1a_\x00?\a\xe3\xa3^\xa3k\xa5w\xa6\xe2\x0e\xc7\xda\xe6\x9bR\xf9\xbd\xf2\x1d,dt qo\xaa\x9c\x15\x87c\xadVc\x11\xe3k\xb3\x17\x80O\xed\x04\xb6up@\xf9X\xe4\xe0\xee\xc1`A\xda\xfd\xadK\xf9c\xa3n\x9c\x01\x0e\x16\xb2\x06Ф\x18\x84\x98\xae\xe2\xe5d|\xac\x11\u0603\xc7NŃѕI\x06{\xdf\x17\xa8\xbb\x0f\xc5=9\x94\b`\xfe\xf5.\x83p4X\xa8\xd7ٛ >Li\r\xdf\xde\xf8\xd7;\xa5>\x9c\xf2\xcba\x87\x8eM\x17\x94\x8b\x86F\xe76X(\xa5\xc6di\xb2O\xff>\x9c\xaa\x1d\x1d\xf8xG\xd7\x00\xd2ñ~9];\x19,\xdc\xe8\xa9\\\x14\xdc5\xbaŖ\x16\xb7\x0e\xa1\x8c\x84\x92\xfc\xef\xf08X\xec\xd0B\x95\x05x\xf1}Q\x908\x05\x948\xf3\xa5\\U\x97\x04`\xe6\xca@\x9ac\x1b\xa1\x94\x13\x80((\x86?[ע>YP\x1a\xe1^\n\xe81D.G(\xb9\a\"\x06\x18ʽ\xa9#\x82\x99+\x03i\xc76\xdcohx\x9d\x8d\xd9\"\x92\xabX\xa0\x00\x1f\xa6\v\xd6\x0e\xe5\x87\xe9b/\x05ve\x80MO\xadQm\f\x11+\v\xb1\xebɦ\f\xcc\xde\x03\xec\xf5r\xfa\xe6\xf4\xdb\xca\xfd \x99b\xd6\x02\xa4\xc5?\b\xc1\xba\x02$\xd2ykPl)\x1a|\xf2\xa0\xa2\xc7<-\fD]W\x191j\xb0\x97Se \xaa\xf9\x0e\xb8\x9b\x1c\v&\xcf\xee\xb53\xec\xbd\xef\xc4A<D?\xdfm\x13\xe9\x15\xba\a*A\x03u\r\xdcFѠI\x91\x14\xddO\xed\xa6N\xb3\xe3\xa1\xe2\xd3\xe5E\xfc ^\xd2\xe3Sv\xb7\xa8\xa2\xa1\x04&\xcb9X\xcb\xd40E\xe9\xed+eR:\xd8T[b8\xdf*e\xf3\x9bB2\x9aǵ\"߱u\xc10\x1d \x9730O\vSW\v\xa5tb?\x9c\xbe\xed\xb7\xfbhU\xdb\xe8\x87)\a=Qެ\x02K\x0f\xabcTDɈ\x97\x8b\x83\xa0\xa2c\x87\xff\xcd\x11\x88}\x14De\bM\xc6'%\xf7\xd4(Xu\xcd\x00ܚGZ\xc1\xa1\xee⪿B)\xa6T\xea\xd9\xc4\xfb\xa1\n\xe96]\x0e\xe1\x05\xfd\xf6K \xf3\xc24\xfe*E\xf0\x92\x8d\x10i\xfc\xa669;-\xf0XL\x822\xd8笱Q\x88\xe3\xf0\x9d#\xf5\xacs6\x98u\xf7AW\xd1r\xe9<f\x93~2_J\xaf\xdb\xe8\xf5Y\xd8X\x99\xb921Ϧ\xbd\x9c\xae;\xcd\xeb\xf3L\x95\xd4\xf8\xf0\xd5n\xa9{\x04\x0e\xb7\xf8\x9b0\xb9\xd4=\xd2ї\xe3\xe1\xe3\xd6\x11\xc1\xa2\xe5\xf5.>:\xb7\xbdQ\x92\x1ae\xa9\xf4L\xda8c\xf0O\xbabpY,\xe7)lN#lz\xba\xe0\xe3\x9d\xd2\n:\x92wx\x8a\x94r\xee\xe6\x96\t9\xa5\xed\xb9\xac\xce\xcf\a\xe4t\x06\x90\xe6\x1dc\xbd\xf9\xd6\xfe\x1f\xa3\xca\x17\x8c\x10n\xcft\xf9ަ|\n\xba\xb9\xef\xb7U\b\xf7o\xeeh\xb4\x1a\xef\xddգ\\p\x19@\x82_\xb8x\xdd\xd9(\x92\xf8\ue51c\xf5\"\x05\xf3 \x03\x84CW\xa83ٕܿ\xd2z&\x8f\v\xff\xf5\x8e\x9f_\xac\x1aO\xd2\xf8^\xef\xf8n3I\xe9E\ff\x8a@\f\xf2\xf7\xf5\xf8ݤArI\U0004f1ab\x11\t\x90\x16@I\xf8\xb9А\x9b\xf1q\xf0\x8f\x8a[\xa5\x12`M\x87\x11\x11|\x81\xf8\xb9А\x8d\xf11\U0008f38b}\x17T\xda\xf3\x96\x8e_Z\n\x9a\x13\xff\xbc\uf131\xad^b6]\xe9\xf8\x99\xd0`<?G\xcf?&n\x85E\x88\x1e\n\x9c\x8f\x15\x8e\xdfD\x03\xb12\x9a\x88\x1a\xe9h\xf6@r\xef\xb7\xde\xcez\xee\x8b\xd3\x03\x96\x8eߊ\x06\xf8w\"#4.\r\xf6\xe7\x11\xef\x01:nL\x04\xf4\xdd\x06\a\x8f\xc0\x91\u007f\x97\x8e\x9f\v\r\x925\x98\x14\xff\xe8\xb8'\t\x84H\xbe\xfa k|\xa6\xd1`\xb54p<\xae\xb5\x19ȹW\v1퇔\x0e\xa0\xe2F\xcf\at\x84\xcf>\x9f/W|\xeeFPH\x06lCB\x05j!+\xe5URr\xc7\xc2M\x9a\x0f\xe8\x118\xfb|@\x87\x06\xf6\xf9\x80\x16\xbe\xd8|@[\x06\xee\x99\x14\x11\r\xc6\xc5\xe3\xda\aK\xb6!\xe3'\x9d\x0f\xe8\x1a\xbeo\xff\xc9#`\x89\xab\x85\xa7+f>`\xdbZ\xa8\x9f\x0f\x88l\x80\rя\xbf\xda\x1f\xca\xce\a\f\xe1\x970\x1f\xd05B\xe9\xf9\x80Է`\xb2\x9a\x0f\x98\x83\xf1\t\x1a\xbd\xd6ñ\x88\xf1\xb5\xd9\v\xce\xf9\x80u\xb5\x10\xd3\v\x82}``\t\v\x00\x00\x02'IDATQ\x98\xbc1\xe3K\xf9Y\xe7\x03J\xde9\xcca>`\x88\u007fi\xe3\xe3\x8c\xc0\x01l\xf6\xf9\x80\\2\xf0t\x99\xbd\xf6p(n|\x13\xe6\x8fo\xfb_\xa65\xffˑl:~ܓ*\xa0\x8b\xab\xb5V\xc6\x18Q\xfe\xf5p\xccF\xc3\x1e\xae\xc7\x11\xb9\xc7\x1f\xd8{\xe1\xea\x1f\xf7\x1e\x98\r\x18\x16\xaeFS~N\x05\xb19\x06\xa1\x8fU\xf9\x1b\xeb\r\xd8\vB'\xe4D5Ӕ\r\x91:\xc2R\r\xb0.\n2(\xa0\xd9\baG\xd9D\fУ\x00\xac\x06\x18P\x04[&d\xd1'P\u007fq\xa6\xe3\xc6b\x9as\x0f\xb0\r0e#\xcc\u03a2\xefΙ\r\xf3\xc6\x12ĺ\x19\xc0\x83\xbf[kZK9G\x10\x92{\xe0\xfeP5`\x94\xffI\bL<\x13\xb3\aD\x06g\xa8t\xbf3\xf3\x01ݔ\xa7\x9f\x0f(;\x1f\x902\x054\xc6\x18\xebkD\xe7\x03*\xeb\xe0\xbd\xd8\xf9\x80v\xe4o\xbb\t\\\xab\x9f\x0f\x18N\r\xa3\xbf\u007fc|\xbe\xfa\x92\xd3\x14=\x1f\x905\xc7F\x8a\x86\x12\x98\xd2\xf3\x01\xf7\x9ac\xf1J/>\x1f\xb0I劙\x0fhG\xbe~>`X\t8\xe7\x03\xaa\x1f\xf7\xc1\x06PJ\xfd\xb9Q\xfal\xe6\x03*\xd5\xcf\a\xcc*\x12\xf7\xf3\x01\x17\xbb\x17\xb1\u007fܿ\xfd\x10\x1aB?\x1f0\x13\x03pk\x1ei\x05\xef\xe7\x03\xe2\xce\aT*\x9f\xf9\x80\xa1̫\x9f\x0f\x98\x01\xbeRY\xbd\x9d\xda\xc0\xb9\\\xa2\x12\xd6\x1e%\xf4\xf3\x01\xfb\xf9\x80Y\xa5\x9f\xec/\xe3\xa9\xd9hc\f\xd4OF\xc5\xdcp\x82bn\x9dJ\xf6\xf3\x01\x99V?\x1f0\x9e_\x9d\x91\xc3Ѷ!\xc6F綍f\x84(K\xa2g]\x9c\x0fXu\xc5\xe02\x89\xb4\xb5\xf5\n\x87\xc3\t\xc9~sL\xa3\xaeK\xac\xc9\xfa\xf9\x80))h\xc9\xe6\x1d]\xbd\t\xed\xff\xd0\U000c5a69\xc9\xf48\xb7\xf3'\xf2Fd\x1b۽\xab\xc7(\x03V^->M\u05ccܾ?y\xa4z\xc1\x83\xb9\xbd\xf4\x1a\xf8\x94RJN|\xfb\xf2/7\xb3ļ\x1a\x86\xbd:V\xbd\x88\xc6\xfc?vm\x03\x8d\xce٠\xb8\x00\x00\x00\x00IEND\xaeB`\x82")
package main
import (
"bufio"
"log"
"net"
)
var err error
func (g *Game) connexion(){
//Connexion vers le serveur
g.conn, err = net.Dial("tcp", g.IpServer+":8080")
if err != nil {
log.Println("Dial error:", err)
return
}
//Attribution du reader et writer au client
g.reader = bufio.NewReader(g.conn)
g.writer = bufio.NewWriter(g.conn)
//Creation des canaux
g.ch1 = make(chan string, 1)
g.ch2 = make(chan string, 1)
//Lancement de la lecture et de l'écriture
go g.readServer() //Lit sur le serveur et écrit sur le canal 1
go g.writeServer() //Ecrit vers le server ce qui est récupéré sur le canal 2
}
func (g *Game) writeServer(){
//Le client récupère un message du canal ch2 pour l'envoyer vers le serveur
for {
msg := <-g.ch2
g.writer.WriteString(msg +"!") // ! marque la fin du message
g.writer.Flush()
log.Println("J'ai écris au serveur ", msg)
}
}
func (g *Game) readServer(){
//Le client récupère un message du serveur pour le stocker dans le canal ch1
for {
msg,error := g.reader.ReadString('!')
if error != nil {
log.Println("Dial error:", err)
return
}
g.ch1 <- msg
log.Println("Canal ok a récupéré "+msg)
}
}
\ No newline at end of file
/*
// Data structure for representing the running field
// used in the game. Provided with a few utilitary
// methods:
// - Draw
// - Reset
*/
package main
import (
"fmt"
"image/color"
"time"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)
type Field struct {
xstart float64 // Position of the start line for the run
xarrival float64 // Position of the arrival line for the run
chrono time.Time // Time recording of the run duration
}
// Reset allows to reset the field state in order to be able to start a new run
func (f *Field) Reset() {
f.chrono = time.Now()
}
// Draw displays the field on the screen
func (f *Field) Draw(screen *ebiten.Image, drawChrono bool) {
ebitenutil.DrawLine(screen, f.xstart, 55, f.xstart, 135, color.White)
ebitenutil.DrawLine(screen, f.xarrival, 55, f.xarrival, 135, color.White)
for i := 0; i < 5; i++ {
ebitenutil.DrawLine(screen, f.xstart, float64(55+i*20), f.xarrival, float64(55+i*20), color.White)
}
if drawChrono {
s, ms := GetSeconds(time.Since(f.chrono).Milliseconds())
ebitenutil.DebugPrintAt(screen, fmt.Sprint(s, ":", ms), screenWidth/2-25, 10)
}
}
/*
// Implementation of the Draw method for the Game structure
// This method is called once at every frame (60 frames per second)
// by ebiten, juste after calling the Update method (game-update.go)
// Provided with a few utilitary methods:
// - DrawLaunch
// - DrawResult
// - DrawRun
// - DrawSelectScreen
// - DrawWelcomeScreen
*/
package main
import (
"fmt"
"image"
"image/color"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)
// DrawWelcomeScreen displays the title screen in the game window
func (g *Game) DrawWelcomeScreen(screen *ebiten.Image) {
ebitenutil.DebugPrintAt(
screen,
fmt.Sprint("Track & Field: BUT2 2022-2023 Edition"),
screenWidth/2-120,
screenHeight/2-20,
)
if !g.allConnected {
// Si les joueurs ne sont pas tous connectés, un message d'attente et le nombre de joueurs connectés s'affichent.
ebitenutil.DebugPrintAt(
screen,
fmt.Sprint("Waiting for players ..."),
screenWidth/2-60,
screenHeight/2+10,
)
ebitenutil.DebugPrintAt(
screen,
fmt.Sprint(g.NombreJoueurs+1)+"/4",
screenWidth/2-20,
screenHeight/2+25,
)
}else{
ebitenutil.DebugPrintAt(
screen,
fmt.Sprint("Press SPACE to start !"),
screenWidth/2-60,
screenHeight/2+10,
) }
ebitenutil.DebugPrintAt(
screen,
fmt.Sprint("Ip : "+g.IpServer), //L'ip du serveur est affichée
screenWidth/2-350,
screenHeight/2+50,
)
}
// DrawSelectScreen displays the runner selection screen in the game window
func (g *Game) DrawSelectScreen(screen *ebiten.Image) {
if (!g.runners[g.numPlayerInt].colorSelected){
ebitenutil.DebugPrintAt(
screen,
fmt.Sprint("Select your player"),
screenWidth/2-60,
20,
)
}else{
ebitenutil.DebugPrintAt(
screen,
fmt.Sprint("Waiting for other players..."), //Si le joueur a choisi sa couleur, un message d'attente s'affiche
screenWidth/2-60,
20,
)
}
xStep := (screenWidth - 100) / 8
xPadding := (xStep - 32) / 2
yPos := (screenHeight - 32) / 2
for i := 0; i < 8; i++ {
options := &ebiten.DrawImageOptions{}
xPos := 50 + i*xStep + xPadding
options.GeoM.Translate(float64(xPos), float64(yPos))
screen.DrawImage(g.runnerImage.SubImage(image.Rect(0, i*32, 32, i*32+32)).(*ebiten.Image), options)
}
for i := range g.runners {
g.runners[i].DrawSelection(screen, xStep, i)
}
}
// DrawLaunch displays the countdown before a run in the game window
func (g *Game) DrawLaunch(screen *ebiten.Image) {
if g.launchStep > 1 {
ebitenutil.DebugPrintAt(screen, fmt.Sprint(5-g.launchStep), screenWidth/2-10, 10)
}
}
// DrawRun displays the current state of the run in the game window
func (g *Game) DrawRun(screen *ebiten.Image, drawChrono bool) {
g.f.Draw(screen, drawChrono)
for i := range g.runners {
g.runners[i].Draw(screen)
}
}
// DrawResult displays the results of the run in the game window
func (g *Game) DrawResult(screen *ebiten.Image) {
ranking := [4]int{-1, -1, -1, -1}
for i := range g.runners {
rank := 0
for j := range g.runners {
if g.runners[i].runTime > g.runners[j].runTime {
rank++
}
}
for ranking[rank] != -1 {
rank++
}
ranking[rank] = i
}
for i := 1; i < g.resultStep && i <= 4; i++ {
s, ms := GetSeconds(g.runners[ranking[i-1]].runTime.Milliseconds())
ebitenutil.DebugPrintAt(screen, fmt.Sprint(i, ". P", ranking[i-1]+1, " ", s, ":" , ms), screenWidth/2-40, 55+ranking[i-1]*20)
}
if g.resultStep > 4{
if !g.askRestart{
ebitenutil.DebugPrintAt(screen, "Press SPACE to restart", screenWidth/2-60, 10)
}else{
//Si le joueur a demandé à recommencer, un message d'attente s'affiche avec le nombre de joueur demandant à recommencé
ebitenutil.DebugPrintAt(screen, "En attente des autres joueurs...", screenWidth/2-60, 10)
ebitenutil.DebugPrintAt(screen, fmt.Sprint(g.NombreJoueurs)+"/4", screenWidth/2, 25)
}
}
}
// Draw is the main drawing function of the game. It is called by ebiten at
// each frame (60 times per second) just after calling Update (game-update.go)
// Depending of the current state of the game it calls the above utilitary
// function to draw what is needed in the game window
func (g *Game) Draw(screen *ebiten.Image) {
screen.Fill(color.RGBA{141, 200, 235, 255})
if g.getTPS {
ebitenutil.DebugPrint(screen, fmt.Sprint(ebiten.CurrentTPS()))
ebitenutil.DebugPrintAt(screen, "P"+fmt.Sprint(g.numPlayerInt+1),0,15)
}
switch g.state {
case StateWelcomeScreen:
g.DrawWelcomeScreen(screen)
case StateChooseRunner:
g.DrawSelectScreen(screen)
case StateLaunchRun:
g.DrawLaunch(screen)
g.DrawRun(screen, false)
case StateRun:
g.DrawRun(screen, true)
case StateResult:
g.DrawResult(screen)
g.DrawRun(screen, false)
}
}
/*
// Implementation of the Layout method for the Game structure
*/
package main
func (g *Game) Layout(width, height int) (int, int) {
return screenWidth, screenHeight
}
/*
// Implementation of the Update method for the Game structure
// This method is called once at every frame (60 frames per second)
// by ebiten, juste before calling the Draw method (game-draw.go).
// Provided with a few utilitary methods:
// - CheckArrival
// - ChooseRunners
// - HandleLaunchRun
// - HandleResults
// - HandleWelcomeScreen
// - Reset
// - UpdateAnimation
// - UpdateRunners
*/
package main
import (
"fmt"
"time"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
)
// HandleWelcomeScreen waits for the player to push SPACE in order to
// start the game
func (g *Game) HandleWelcomeScreen() bool {
return inpututil.IsKeyJustPressed(ebiten.KeySpace)
}
func (g *Game) ChooseRunners() (done bool) {
//Choix manuel de la couleur du joueur en tenant compte des couleurs interdites
//done devient vrai lorsque le joueur presse la touche espace sur une couleur qui n'est pas déjà séléctionnée.
done = false
done = g.runners[g.numPlayerInt].ManualChoose(g.ch2,g.bannedColors)
return done
}
// HandleLaunchRun countdowns to the start of a run
func (g *Game) HandleLaunchRun() bool {
if time.Since(g.f.chrono).Milliseconds() > 1000 {
g.launchStep++
g.f.chrono = time.Now()
}
if g.launchStep >= 5 {
g.launchStep = 0
return true
}
return false
}
// UpdateRunners loops over all the runners to update each of them
func (g *Game) UpdateRunners() {
g.runners[g.numPlayerInt].ManualUpdate(g.ch2) //update current runner position and speed
var speed float64 = 0.0
select{
case infoRunner:=<-g.ch1:
if infoRunner == "allArrived!"{
g.allArrived = true
}else{
numConn := convertToInt(infoRunner[:1])
speed = convertToFloat(infoRunner[1:7])
g.runners[numConn].speed = speed
g.runners[numConn].framesSinceUpdate = 0
}
default:
}
for i:=0;i<4;i++{
if i != g.numPlayerInt{
if g.runners[i].framesSinceUpdate > g.runners[i].maxFrameInterval {
g.runners[i].speed = 0
g.runners[i].framesSinceUpdate = 0
}
g.runners[i].UpdatePos()
g.runners[i].framesSinceUpdate++
}
}
}
// CheckArrival loops over all the runners to check which ones are arrived
func (g *Game) CheckArrival() (finished bool) {
finished = true
for i := range g.runners {
g.runners[i].CheckArrival(&g.f)
finished = finished && g.runners[i].arrived
}
return finished
}
// Reset resets all the runners and the field in order to start a new run
func (g *Game) Reset() {
for i := range g.runners {
g.runners[i].Reset(&g.f)
}
g.f.Reset()
g.state = StateLaunchRun
g.resultSent = false
g.allArrived = false
g.runFinishedSent = false
g.resultGet = 0
g.resultStep = 0
g.askRestart = false
g.allRestart = false
}
// UpdateAnimation loops over all the runners to update their sprite
func (g *Game) UpdateAnimation() {
for i := range g.runners {
g.runners[i].UpdateAnimation(g.runnerImage)
}
}
// HandleResults computes the resuls of a run and prepare them for
// being displayed
func (g *Game) HandleResults() (done bool) {
if time.Since(g.f.chrono).Milliseconds() > 1000 || inpututil.IsKeyJustPressed(ebiten.KeySpace){
g.resultStep++
g.f.chrono = time.Now()
}
if g.resultStep > 4 && inpututil.IsKeyJustPressed(ebiten.KeySpace){
if !g.askRestart{
g.ch2<-"restart"
g.askRestart = true
return true
}
}
return false
}
// Update is the main update function of the game. It is called by ebiten
// at each frame (60 times per second) just before calling Draw (game-draw.go)
// Depending of the current state of the game it calls the above utilitary
// function and then it may update the state of the game
func (g *Game) Update() error {
switch g.state {
case StateWelcomeScreen:
done := g.HandleWelcomeScreen() //Renvoit vrai si la touche espace est pressée
if !g.allConnected{
select{
case temp := <-g.ch1 :
if temp == "AllConnected!"{
//Si le serveur envoit AllConnected!, alors tous les joueurs sont connectés
g.allConnected = true
}else{
if g.numPlayerString == ""{
//Si le joueur n'a pas encore de numéro attribué, on le lui en attribu un sous forme d'entier et un autre en chaine de caractères
g.numPlayerString = temp
g.numPlayerString = g.numPlayerString[:1]
g.numPlayerInt = convertToInt(g.numPlayerString)
g.ch2<-"Connected"
}
//Mise à jour du nombre de joueur connectés
g.NombreJoueurs = convertToInt(temp[:1])
}
default:
}
}
if g.allConnected && done{
g.state++
}
case StateChooseRunner:
done := g.ChooseRunners() //Renvoit vrai si le joueur a choisi son personnage
select{
case posPlayer:=<-g.ch1 :
if posPlayer == "AllChosen!"{
g.allChosen = true
}else{
if posPlayer[:3]=="ban"{
//Si le client recoit un message commencant par ban, alors la valeur qui suit est celle d'une couleur déjà selectionnée
g.bannedColors = append(g.bannedColors,convertToInt(posPlayer[3:4]))
}else{
colorScheme := convertToInt(posPlayer[1:2])
numconn := convertToInt(posPlayer[:1]) //Met a jour la position des autres curseurs
g.runners[numconn].PlayersChoose(colorScheme)
}
}
default:
}
if done && g.allChosen{
g.UpdateAnimation()
g.state++
}
case StateLaunchRun:
done := g.HandleLaunchRun()
if done {
g.state++
}
case StateRun:
g.UpdateRunners()
finished := g.CheckArrival()
g.UpdateAnimation()
if finished{
if !g.runFinishedSent{
g.ch2<-"OK"
g.runFinishedSent = true
}
}
if g.allArrived{
g.state++
}
case StateResult:
if !g.resultSent{
g.ch2<-fmt.Sprint(g.runners[g.numPlayerInt].runTime)
g.resultSent = true
}
if g.resultGet!=4{
//Tant que les 4 résultats n'ont pas été envoyés
select{
case seconds:=<-g.ch1:
numConn := convertToInt(seconds[:1])
s := seconds[1:7]
g.runners[numConn].runTime,_ = time.ParseDuration(s+"s")
g.resultGet++
default:
}
}
g.HandleResults()
if g.askRestart{
//Si on a demandé à recommencer
select{
case answerRestart:=<-g.ch1:
if answerRestart == "allRestart!"{
g.allRestart = true
}else{
g.NombreJoueurs = convertToInt(answerRestart[:1])
}
default:
}
if g.allRestart{
g.Reset() //Reinitialisation des valeurs et retour à la course
}
}
}
return nil
}
/*
// Data structure for representing a game. Implements the ebiten.Game
// interface (Update in game-update.go, Draw in game-draw.go, Layout
// in game-layout.go). Provided with a few utilitary functions:
// - initGame
*/
package main
import (
"bufio"
"bytes"
"course/assets"
"image"
"log"
"time"
"net"
"github.com/hajimehoshi/ebiten/v2"
)
type Game struct {
NombreJoueurs int
numPlayerString string
numPlayerInt int
state int // Current state of the game
runnerImage *ebiten.Image // Image with all the sprites of the runners
runners [4]Runner // The four runners used in the game
f Field // The running field
launchStep int // Current step in StateLaunchRun state
resultStep int // Current step in StateResult state
getTPS bool // Help for debug
allConnected bool // True when all the clients are connect
allChosen bool // True when all the clients chose their runner
IpServer string // IP du serveur
conn net.Conn // Connection between the server and the client
ch1,ch2 chan string // Creation of two chan of type string
writer *bufio.Writer // Creation of a writer
reader *bufio.Reader // Creation of a reader
bannedColors []int // List of chosen colors
allArrived bool // True when all the clients have finish the course
resultSent bool // True when the client have send its result
resultGet int // Number of the result read
runFinishedSent bool // True when the client have finish the course and have sent OK to the server
askRestart bool // True when the client press SPACE to restart the course
allRestart bool // True when all the clients want to restart the course
}
// These constants define the five possible states of the game
const (
StateWelcomeScreen int = iota // Title screen
StateChooseRunner // Player selection screen
StateLaunchRun // Countdown before a run
StateRun // Run
StateResult // Results announcement
)
// InitGame builds a new game ready for being run by ebiten
func InitGame() (g Game) {
// Define variables to false
g.allConnected = false
g.allChosen = false
// Open the png image for the runners sprites
img, _, err := image.Decode(bytes.NewReader(assets.RunnerImage))
if err != nil {
log.Fatal(err)
}
g.runnerImage = ebiten.NewImageFromImage(img)
// Define game parameters
start := 50.0
finish := float64(screenWidth - 50)
frameInterval := 20
// Create the runners
for i := range g.runners {
g.runners[i] = Runner{
xpos: start, ypos: 50 + float64(i*20),
maxFrameInterval: frameInterval,
colorScheme: 0,
}
}
// Create the field
g.f = Field{
xstart: start,
xarrival: finish,
chrono: time.Now(),
}
return g
}
module course
go 1.18
require github.com/hajimehoshi/ebiten/v2 v2.0.6
require (
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2 // indirect
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect
golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f // indirect
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 // indirect
)
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2 h1:Ac1OEHHkbAZ6EUnJahF0GKcU0FjPc/V8F1DvjhKngFE=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY=
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/hajimehoshi/bitmapfont/v2 v2.1.0/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
github.com/hajimehoshi/ebiten/v2 v2.0.6 h1:sHNymgI+q80xasP69oFyrpup6r2qCNsKxqwsGEh6PWE=
github.com/hajimehoshi/ebiten/v2 v2.0.6/go.mod h1:uS3OjMW3f2DRDMtWoIF7yMMmrMkv+fZ6pXcwR1pfA0Y=
github.com/hajimehoshi/file2byteslice v0.0.0-20200812174855-0e5e8a80490e/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
github.com/hajimehoshi/go-mp3 v0.3.1/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/hajimehoshi/oto v0.6.8/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/jakecoffman/cp v1.0.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg=
github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk=
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f h1:aEcjdTsycgPqO/caTgnxfR9xwWOltP/21vtJyFztEy0=
golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 h1:bNEHhJCnrwMKNMmOx3yAynp5vs5/gRy+XWFtZFu7NBM=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20201009162240-fcf82128ed91/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
/*
// Implementation of a main function setting a few characteristics of
// the game window, creating a game, and launching it
*/
package main
import (
"flag"
_ "image/png"
"log"
"github.com/hajimehoshi/ebiten/v2"
)
const (
screenWidth = 800 // Width of the game window (in pixels)
screenHeight = 160 // Height of the game window (in pixels)
)
func main() {
var getTPS bool
flag.BoolVar(&getTPS, "tps", false, "Afficher le nombre d'appel à Update par seconde")
//Ajout d'un flag pour rentrer l'ip de connexion du serveur.
var IpServer string
flag.StringVar(&IpServer, "ip", "", "IP du serveur")
flag.Parse()
ebiten.SetWindowSize(screenWidth, screenHeight)
ebiten.SetWindowTitle("BUT2 année 2022-2023, R3.05 Programmation système")
g := InitGame()
// Assiniation du flag IpServer à la variable IpServer de game.
g.IpServer = IpServer
// Lancement de la fonction connexion() de client.go qui va
// faire la connexion client-server et lancer les deux go routines
// une pour lire et une pour écrire.
g.connexion()
g.getTPS = getTPS
g.ch2 <- "OK" // Envoi d'un message OK au serveur.
// Recupération des erreurs
err := ebiten.RunGame(&g)
log.Print(err)
}
-Réalisation du «net code» d’un jeu vidéo simple-
Partie 1 : un premier serveur:
1 Étude du code fourni:
Comment se déroule un tour de boucle de la fonction Update() de game-update.go ?
\ No newline at end of file
/*
// Data structure for representing the four runners
// used in the game. Provided with a few utilitary
// methods:
// - CheckArrival
// - Draw
// - DrawSelection
// - ManualChoose
// - ManualUpdate
// - RandomChoose
// - RandomUpdate
// - Reset
// - UpdateAnimation
// - UpdatePos
// - UpdateSpeed
*/
package main
import (
"math"
"fmt"
"image"
"time"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/inpututil"
)
type Runner struct {
xpos, ypos float64 // Position of the runner on the screen
speed float64 // Current speed of the runner
framesSinceUpdate int // Number of frames since last speed update
maxFrameInterval int // Maximum number of frames between two speed updates
arrived bool // Tells if the runner has finished running or not
runTime time.Duration // Records the duration of the run for ranking
image *ebiten.Image // Current image used to display the runner
colorScheme int // Number of the color scheme of the runner
colorSelected bool // Tells if the color scheme is fixed or not
animationStep int // Current step of the runner animation
animationFrame int // Number of frames since the last animation step
colorchosen bool
}
// ManualUpdate allows to use the keyboard in order to control a runner
// when the game is in the StateRun state (i.e. during a run)
func (r *Runner) ManualUpdate(ch chan string) {
r.UpdateSpeed(inpututil.IsKeyJustPressed(ebiten.KeySpace),ch)
r.UpdatePos()
}
// UpdateSpeed sets the speed of a runner. It is used when the game is in
// StateRun state (i.e. during a run)
func (r *Runner) UpdateSpeed(keyPressed bool, ch chan string) {
if !r.arrived {
r.framesSinceUpdate++
if keyPressed {
r.speed = (math.Round((1500 / float64(r.framesSinceUpdate*r.framesSinceUpdate*r.framesSinceUpdate))*1000))/1000
if r.speed > 10 {
r.speed = 10
}
r.framesSinceUpdate = 0
ch<-fmt.Sprint(r.speed+0.0001)
} else if r.framesSinceUpdate > r.maxFrameInterval {
r.speed = 0
}
}
}
// UpdatePos sets the current (x) position of a runner according to the current
// speed and the previous (x) position. It is used when the game is in StateRun
// state (i.e. during a run)
func (r *Runner) UpdatePos() {
if !r.arrived { //La vitesse est bien envoyé et récupéré mais on ne porend pas en compte le nombre d'images par secondes, la vitesse ne s'applique que sur une frame. Une variable nous echappe.
r.xpos += r.speed
}
}
// UpdateAnimation determines the next image that should be displayed for a
// runner, depending of whether or not the runner is running, the current
// animationStep and the current animationFrame
func (r *Runner) UpdateAnimation(runnerImage *ebiten.Image) {
r.animationFrame++
if r.speed == 0 || r.arrived {
r.image = runnerImage.SubImage(image.Rect(0, r.colorScheme*32, 32, r.colorScheme*32+32)).(*ebiten.Image)
r.animationFrame = 0
} else {
if r.animationFrame > 1 {
r.animationStep = r.animationStep%6 + 1
r.image = runnerImage.SubImage(image.Rect(32*r.animationStep, r.colorScheme*32, 32*r.animationStep+32, r.colorScheme*32+32)).(*ebiten.Image)
r.animationFrame = 0
}
}
}
// ManualChoose allows to use the keyboard for selecting the appearance of a
// runner when the game is in StateChooseRunner state (i.e. at player selection
// screen)
func (r *Runner) ManualChoose(ch chan string, bannedColors []int) (done bool) {
r.colorSelected = (!r.colorSelected && inpututil.IsKeyJustPressed(ebiten.KeySpace)) && !inTab(bannedColors,r.colorScheme) || r.colorSelected
if !r.colorSelected {
if inpututil.IsKeyJustPressed(ebiten.KeyRight) {
r.colorScheme = (r.colorScheme + 1) % 8
ch <- fmt.Sprint(r.colorScheme)
} else if inpututil.IsKeyJustPressed(ebiten.KeyLeft) {
r.colorScheme = (r.colorScheme + 7) % 8
ch <- fmt.Sprint(r.colorScheme)
}
}else{
if !r.colorchosen{
ch <- fmt.Sprint(r.colorScheme)+"chosen"
r.colorchosen = true
}
}
return r.colorSelected
}
// RandomChoose allows to randomly select the appearance of a
// runner when the game is in StateChooseRunner state (i.e. at player selection
// screen)
func (r *Runner) PlayersChoose(colorScheme int) (done bool) {
r.colorScheme = colorScheme
return r.colorSelected
}
// CheckArrival allows to test if a runner has passed the arrival line
func (r *Runner) CheckArrival(f *Field) {
if !r.arrived {
r.arrived = r.xpos > f.xarrival
r.runTime = time.Since(f.chrono)
}
}
// Reset allows to reset a player state in order to be able to start a new run
func (r *Runner) Reset(f *Field) {
r.xpos = f.xstart
r.speed = 0
r.framesSinceUpdate = 0
r.arrived = false
r.animationStep = 0
r.animationFrame = 0
}
// Draw draws a runner on screen at the good position (defined by xpos and ypos)
func (r *Runner) Draw(screen *ebiten.Image) {
options := &ebiten.DrawImageOptions{}
options.GeoM.Translate(r.xpos-16, r.ypos-16)
screen.DrawImage(r.image, options)
}
// DrawSelection draws the current selection of a runner appearance for the
// player select screen
func (r *Runner) DrawSelection(screen *ebiten.Image, xStep, playerNum int) {
xMod := 32
if (playerNum/2)%2 == 0 {
xMod = -32
}
xPadding := (xStep + xMod) / 2
xPos := 43 + xStep*r.colorScheme + xPadding
yMod := 32
if playerNum%2 == 0 {
yMod = -62
}
yPos := (screenHeight + yMod) / 2
ebitenutil.DebugPrintAt(screen, fmt.Sprint("P", playerNum+1), xPos, yPos)
}
#!/bin/bash
go build
gnome-terminal -t 'server' -- go run ../server/server.go &
sleep 0.5
gnome-terminal --tab --tab --tab --tab -- ./course -tps ip=localhost &
\ No newline at end of file
/*
// Implementation of a few utilitary functions:
// - getSeconds
*/
package main
import( "strconv" )
import("log")
// GetSeconds splits a time in milliseconds into seconds and milliseconds
func GetSeconds(d int64) (s, ms int64) {
s = d / 1000
ms = d - s*1000
return s, ms
}
// Convert string into integer with strvconv.Atoi()
func convertToInt(s string)(integer int){
integer,err := strconv.Atoi(s)
if err != nil {
log.Println("Convert Error:", err)
return
}
return integer
}
// Convert string into Float with strvconv.ParseFloat()
func convertToFloat(s string) (float float64){
float,err:= strconv.ParseFloat(s, 64)
if err != nil {
log.Println("Convert Error:", err)
return
}
return float
}
// Return true if the item in on the array arr
func inTab(arr []int, item int) bool {
if len(arr)==0{
return false
}else{
for i := 0; i < len(arr); i++ {
if arr[i] == item {
return true
}
}
}
return false
}
package main
import (
"log"
"net"
"bufio"
"sync"
"fmt"
)
const (
StateWelcomeScreen int = iota // Title screen
StateChooseRunner // Player selection screen
StateRun // Run
StateResult // Results announcement
)
var readerConnections = make([]*bufio.Reader,0,4) //Tableau contenant chaque reader de chaque connexion
var writerConnections = make([]*bufio.Writer,0,4) //Tableau contenant chaque writer de chaque connexion
var w sync.WaitGroup // Création d'un Waiting group pour synchroniser les Go routines
var stateRout = 0 //Compteur de reponse des clients
var state = 0 //Etape de progression du serveur
func readConn(Conn *bufio.Reader,i int){
var strI string= fmt.Sprint(i) //Stockage de i en format string
for{
// Attente d'un message
log.Println("Je veux un message")
msg,err := Conn.ReadString('!')
if err != nil {
log.Println("Error:", err)
return
}
msg = msg[:len(msg)-1]
log.Println("Je lis un message : "+msg)
//ANALYSE DU MESSAGE ET MODIFICATION//
switch state {
case StateWelcomeScreen:
//Si le client est connecté, stateRout recoit une réponse.
//Quand les 4 clients sont connectés, le serveur envoit AllConnected à tous les clients
if (msg == "Connected"){
stateRout++
}
if stateRout == 4{
state++
for j:=0;j<4;j++{
writerConnections[j].WriteString("AllConnected!")
writerConnections[j].Flush()
}
stateRout = 0
}
case StateChooseRunner:
//Dès qu'un joueur a choisi sa couleur, le serveur renvoit aux autres clients la couleur choisi qui sera ensuite bannie. StateRout incrémente.
if (msg[1:] == "chosen"){
for j:=0;j<4;j++{
writerConnections[j].WriteString("ban"+fmt.Sprint(msg[:1]+"!"))
writerConnections[j].Flush()
}
stateRout++
}else{
//Le serveur renvoit le numéro du client suivit de la position de son curseur de selection aux autres clients.
for j:=0;j<4;j++{
writerConnections[j].WriteString(strI+msg+"!")
writerConnections[j].Flush()
}
}
//Si tous les clients ont choisi le personnage, le serveur envoit AllChosen à tous les clients
if stateRout == 4{
state++
for j:=0;j<4;j++{
writerConnections[j].WriteString("AllChosen!")
writerConnections[j].Flush()
}
stateRout = 0
}
case StateRun:
//Si le client est arrivé, stateRoute incrémente, sinon le serveur renvoit sa vitesse aux autres clients.
if msg[:2] == "OK"{
stateRout++
}else{
for j:=0;j<4;j++{
if j != i{
writerConnections[j].WriteString(strI+msg+"!")
writerConnections[j].Flush()
}
}
}
//Si tous les clients sont arrivés, le serveur renvoit allArrived à tous les clients.
if stateRout ==4{
for j:=0;j<4;j++{
writerConnections[j].WriteString("allArrived!")
writerConnections[j].Flush()
}
state++
stateRout = 0
}
//Le serveur renvoit les temps de chaque joueur, si un client envoi restart stateRout incrémente
// Et quand stateRout vaut 4 alors le serveur envoi allRestart à tous les clients et le serveur
// revient au StateRun.
case StateResult:
if msg == "restart"{
stateRout++
for j:=0;j<4;j++{
writerConnections[j].WriteString(fmt.Sprint(stateRout)+"!")
writerConnections[j].Flush()
}
}else{
for j:=0;j<4;j++{
writerConnections[j].WriteString(strI+msg+"!")
writerConnections[j].Flush()
}
}
if stateRout == 4{
for j:=0;j<4;j++{
writerConnections[j].WriteString("allRestart!")
writerConnections[j].Flush()
}
state = StateRun
stateRout = 0
}
}
}
}
func listenEachConn(readerConnections []*bufio.Reader){
//On appelle une go routine par connexion qui va se charger de communique avec le client depuis le serveur.
w.Add(4)
go readConn(readerConnections[0],0)
go readConn(readerConnections[1],1)
go readConn(readerConnections[2],2)
go readConn(readerConnections[3],3)
w.Wait()
}
func main() {
// Création d'un listener
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Println("listen error:", err)
return
}
// Acceptation des connections jusqu'à avoir 4 connections
for (len(readerConnections) != 4){
conn, err := listener.Accept()
if err != nil {
log.Println("accept error:", err)
return
}
// Initialisation du reader et writer de la connexion et ajout de ces derniers aux tableaux.
var reader = bufio.NewReader(conn)
var writer = bufio.NewWriter(conn)
readerConnections = append(readerConnections,reader) //Créations des 4 readers et writers des 4 connections
writerConnections = append(writerConnections,writer)
//Envoit du nombre de clients connectés aux clients
for _,writ := range writerConnections{
writ.WriteString(fmt.Sprint(len(readerConnections)-1)+"!")
writ.Flush()
}
}
// Lancement de la fonction listenEachConn
listenEachConn(readerConnections)
}
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Veuillez vous inscrire ou vous pour commenter