From cc7e28af938731fa32f08578ad8ee4c05d42573c Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Mon, 11 Nov 2013 09:39:30 +0100 Subject: [PATCH 1/9] Ignore bin/, target/ --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1f01d76e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +kryonet/bin/ +kryonet/target/ From 26b878b2d194d05a6479c0c45db535008ca35890 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Mon, 11 Nov 2013 09:46:52 +0100 Subject: [PATCH 2/9] Initial Maven POM with tricky non-Maven dependency download An initial call of "mvn validate" (without "compile") automatically downloads non-Maven dependencies Jsonbeans and Minlog, unzips them and installs the JARs in the local Maven repo. Any subsequent Maven call can then do a normal build (current version is 2.21-SNAPSHOT). Download, unpack and install actions are only performed once, as long as ~/.m2/repository/.cache/maven-download-plugin is not deleted. BTW: The real fix for this ugly non-Maven download would be to release Minlog and Jsonbeans on Maven Central or any other public repo. --- kryonet/pom.xml | 122 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 kryonet/pom.xml diff --git a/kryonet/pom.xml b/kryonet/pom.xml new file mode 100644 index 00000000..60a47d2d --- /dev/null +++ b/kryonet/pom.xml @@ -0,0 +1,122 @@ + + + 4.0.0 + + com.esotericsoftware + kryonet + 2.21-SNAPSHOT + + + src + test + bin + bin + + + + com.googlecode.maven-download-plugin + maven-download-plugin + 1.1.0 + + + download-jsonbeans + validate + + wget + + + https://jsonbeans.googlecode.com/files/jsonbeans-0.5.zip + true + 8aca6c467d2a350434f08dd0dce4ba9a + + + + download-minlog + validate + + wget + + + https://minlog.googlecode.com/files/minlog-1.2.zip + true + 32552fea7ca0189dde2d15e6948acecb + + + + + + maven-install-plugin + 2.3.1 + + + install-jsonbean + validate + + install-file + + + ${project.build.directory}/jsonbeans-0.5/jsonbeans-0.5.jar + com.esotericsoftware + jsonbeans + 0.5 + jar + true + true + + + + install-minlog + validate + + install-file + + + ${project.build.directory}/minlog-1.2.jar + com.esotericsoftware + minlog + 1.2 + jar + true + true + + + + + + + + + + com.esotericsoftware.kryo + kryo + 2.20 + + + org.ow2.asm + asm + 4.0 + + + org.objenesis + objenesis + 1.2 + + + com.esotericsoftware + jsonbeans + 0.5 + + + com.esotericsoftware + minlog + 1.2 + + + junit + junit + 4.11 + test + + + From 0d0c240e8fb57e29e97368f6d98c6bbb646ab51f Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Mon, 11 Nov 2013 09:52:59 +0100 Subject: [PATCH 3/9] Remove unnecessary dependencies in lib/, build/ The dependencies are now found by Maven. TODO: Check if minlog-none and kryo-debug are really needed and have to be somehow re-integrated into the build. --- kryonet/build/junit-4.6.jar | Bin 218226 -> 0 bytes kryonet/build/kryo-2.20.jar | Bin 134540 -> 0 bytes kryonet/build/minlog-none-1.2.jar | Bin 2788 -> 0 bytes kryonet/lib/asm-4.0.jar | Bin 46018 -> 0 bytes kryonet/lib/jsonbeans-0.5.jar | Bin 49943 -> 0 bytes kryonet/lib/kryo-debug-2.20.jar | Bin 140366 -> 0 bytes kryonet/lib/minlog-1.2.jar | Bin 3085 -> 0 bytes kryonet/lib/objenesis-1.2.jar | Bin 36034 -> 0 bytes kryonet/lib/reflectasm-1.07.jar | Bin 13288 -> 0 bytes 9 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 kryonet/build/junit-4.6.jar delete mode 100644 kryonet/build/kryo-2.20.jar delete mode 100644 kryonet/build/minlog-none-1.2.jar delete mode 100644 kryonet/lib/asm-4.0.jar delete mode 100644 kryonet/lib/jsonbeans-0.5.jar delete mode 100644 kryonet/lib/kryo-debug-2.20.jar delete mode 100644 kryonet/lib/minlog-1.2.jar delete mode 100644 kryonet/lib/objenesis-1.2.jar delete mode 100644 kryonet/lib/reflectasm-1.07.jar diff --git a/kryonet/build/junit-4.6.jar b/kryonet/build/junit-4.6.jar deleted file mode 100644 index 00770a4fd44fae60d3aa64ff9e2e4e8ea562d072..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218226 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≥M-(wSq9qZNBBhTHBjLpxE<#In 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

LW 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 jL-K*ktd0)d~ diff --git a/kryonet/build/kryo-2.20.jar b/kryonet/build/kryo-2.20.jar deleted file mode 100644 index 428afb3cc6827ee2a53b79600b938b6d530cf9bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 134540 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ˆM73EDPMXjUaXND-CeNpUB#mXF- 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%0Dmmj*h#IoqKD1oxP6V*ZT!d54PN$ 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%0As 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ⅅYEUp zUyVrcHt2sWS#I6;JJ!l_{9ShX>Si)u1<-LG;6(iDL}Mua zKVbhXb8T&`V`}XDJKRwBds^d{WPz`J>Hh)xm#?-FGPAUG`hy+F7GoOWg+-DA z8YXy!5;EC8rR2Ir0>^c?SR*I1yaX#?E#v^I&=tg9`9DRx@tlkp`1NKZ-NAl0g9yy& z@DD&hsIEXdD&7Y9_q-0s@#c;}+o+pF;6Ok=VqFjC&guVq$A3kre})VIq5T$yUwqE= zBGuSMGJ>lj3$?io=CYMsAEb*}?v_zXpWv3Fy?_J^g#SE0waZ$hzC>f586f@Ba0e_Hv2e zt1C7i;L6SKPm69C>1(T)TWWJ(pVTfFj@7&(0*S!?l89f@zH%<|!|(n{RQ+FTg7S@Y=How~I!v7KQ;s)R~QQSbBm0tU_){k9b{!E@#-fx^ma6y$$Ee)cub{ao15U zCrG+Fd9v2uhH}|ju1%TOb9Y^byqt~Zs-{l8|5L~tCeNQ?l;7lGx=zF8JR`za264Xm zPieT$_Vp|{*D)_AJm9*58PxWtm;m25oW#)PdW`_`&L5_d;x?Ge-2U_C a?BD(i9Ek1#mpXty9YCn2a2nWGf&3r;x5#b) diff --git a/kryonet/build/minlog-none-1.2.jar b/kryonet/build/minlog-none-1.2.jar deleted file mode 100644 index 50bd8d2cd2368c55b45fe40e76243207d85db349..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2788 zcmai$2T+sQ7RMu@7crp)rASFoN&rzo4frC0bR(dEpg^dh#ZW_U20{^a0jVn@JP}wS z2n!NWHza^kMViuEBqAuCSXN)6`$QdC&o_7G%$@T;-}&x0_dh?BDUeeXz{`5vB0~{? zZ-NiN2{1RXMyeTG7^;740|0;klqnA{yMp7NGSrr0QC7uQu{qMh*wDbr8g6d5V&2hi zW~v775i(U%?&|2Sw$$j;nxOl`P0Uq|&3l5%IQg<)O44@a?)Bu%da;-34wIlu*obEp zzYtf!zkt5NEc-9{Z~HvR(F#Q33D#@ai#-Vc=b<(tzW)kN)~*ZISAFXWv41Mu0CN zK1G=)2YunHA9nC^?>sh@b&(03;DRxMc>R3u5@p{K^yAs6jr^6-fXD~0!rvFo4`n>;|*ywMEUr;PS1oqo|aup zC3pP=gB+tKV002897(Y*f|UFNC$EMp30W0*4a^2M6nFMN61i=6Iw3x(X~u2=s&=}! z!^^9sN~k(_^oiAB7yH?_V7mv6BqLm+vo~5dsg*8Sjm|2kabe|+ zPM(r`xO)2*9L&Aj?SM-ddI-OxxD?; zsF?On+SO*w{g9TCOJvXtJ)tOF#p%FX z{bbRaEbAO4x#L!V#X;lh3~ipK$54{1fONGf?>*kJm<#@xannhD4!Y61NE6rmy#PHrpP zk?G)5SfxUpJrx=m*C7%%wyI8=9(xUlDwHnNK8bC4zk7ED|L7>ZiLR|-q%lR+i?fe} zKp|^#VvMnt$qBXj8)&5u==c$X!iA4IGeQ)5ohNZ~(Q|33H@WQ@Ij1wB^MN?ot8N8< zD8qy68T8^}<$J_>hCOChg#ith<|i7ctWEop`^G7I{GOg3ItF_4RQSD2*CenpSs1rX zc2Zui#VsOkaB1&YlZPVB7ad`dPlz1Lt83cZjofK(LZ!1cQj)~4#>hvx(ZNTZ&RF7*HY`i#9 zsWm(v9rb}D24jno;|#gU46-3;bR;-i5N<$bsaMcD+U)w3>m+G+Z#`b9DLk}21aiB+ zBMerTqduc0XnPH5Gqg|IMr$;v^Y#)zTv(IwhgA-`3MDG{-<*3@)*UHEB+17wZ20BD zp0`HZRVzm&3e((gTRWI|94Bg`1Rt~tNtGhVcvV;o5+&M-w5DN{;}z&rX{jw~;QHi0 z^JdcM{`QqR1GNp6&k^8+Ps&W?cG- zdUy4FK_E!>$Qipi{@0IaNbNec5vsWQV3`iN_6%jy)e=j)sj7r8l(Fkr7-=vB3^YlxpJ9|3r|ah2KpGgSr*2$KZtF;EW{f zlb&Q&v3>*xOjqd5qBm2dsUnONBU@|y?7;JG8#y17T2Ha6;*nvkrs6SG){9^FuhMdjNWu;%G-yjdW z13zGEI@#>d!b-oe%GL@tt!xH^m9iB-VVA92u<2zpc5U0D#aEX3{_MVWD>lt+#_3;l r`jKsZJlb#n7~AbO<1*L3&&cZh#rsgETr3j>0C-r>OV&8_&$#+OM2|)~ diff --git a/kryonet/lib/asm-4.0.jar b/kryonet/lib/asm-4.0.jar deleted file mode 100644 index 6d63075eb7331f7120ef25603a2ade856d12f715..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46018 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
7;rk8F-KO^;1w1Yj>JIMA-C>v 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%0D}Oc08toOq5P&TObip8)@U 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 diff --git a/kryonet/lib/jsonbeans-0.5.jar b/kryonet/lib/jsonbeans-0.5.jar deleted file mode 100644 index 1dcd9d85904bf3244d82b5fb7d4a92dccd9baae3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49943 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 diff --git a/kryonet/lib/kryo-debug-2.20.jar b/kryonet/lib/kryo-debug-2.20.jar deleted file mode 100644 index aabae511f87b55af256bf0e1419bb6b4f3f4b880..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 140366 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

  • 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 z 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 z z9BtGfk>dcqQtzr0QMtHnJAb6TfpxURXP4*qH4FI?4JTM~?HIe9sAfxswv$jJE3Mao>idwfUcQVk9qlOcf-p?H}c=#5$S2*b$I!9 zEKf~B!H)L;MDfwROop4==c%{=$)F#N=(N9wr*~~;WS{Kc7eBxcM6T*$WsaP`DV!J zJtQY_X&WRmfaLH%oBS})fpu7*bESsF(HY^q&YBYjBqI&eO7+Mn(7TO|5ND# z*ZkS@gMi<9z^@^U-xu7Jf}-%N?Cb~viM`{(Q)o!8TRtlg_Tm`?#%p>5qi5Uan zvUGtUtH1@qizChna(;?IIssvVC-~YgBFL&3odaWlpC^B(ZF6F}LJ#Ri^wJt>7*5IA7Bw)_{N zDWE3DvqOsxNLhjwha+7BLT;A6Sk%nfqOp(jA|@Wp-+#~yFewhYh6nz#Agpeo3PfSj z$l1;QpojZ{Q+~*CC&nYD`VL@a5AbVYGE3m;VlWmwX_~F#K@7x$dyUbxcWPx!iydH$ z1u%w(^?dCZR?kZ?2Dx+RKkcF2yMtsj&p2C(3xtw&AYhUXP?1~wNi1>@p@ejp%6YL^ za@i^N=wCpPD*=uK82~)7hyEhQj{{psE>=z)_E`tkKj*jLaI!E2{xetzP@y8iKDzc^ zQp-#`09Gb}2El*=Y02^bClH^Kx7(7T<=wUSxunzZ++acopNJf}L@Dk8ydQq4*z$UM zG|U3v!~yFhU7kvA4wos#L2QAeR>ivM6my5c1Fo0^@#A&=>FH;e~D}&#q+LlL3N!18~5Y3~92NSU6CNL=oit zhdWD2awsce*&)EB7I13#d~fo`FM=Z`c0WHILcB(oLFP=0gd`xN6p#U5Gj`wnMKZ`7 zd-H)cqpH2Fvm2g3ev3|kW`0q{6`%xnU{UZjGQ)2r2;p+X6E{biPt_`GXxnYQ|I$hXX&5cBcTV=btrH(R&T>(%iEQ&?^k) z726?w;&7e~SiLT#g&N6Y`eGnvt^=BY2j_++tbXTGRttOpa%>kl3amEhocRtw(tx`6 z!x*q_!2pke;UAI3_aMshM_4hTZXY*hqAcPZqUWq(d@RDRP$pm@IcaS z!sJ%C;uoKe))(hGhBZVWeQ9N)I=6L5hqyf~Z)4AoW^3l+YAMdye% z%|;^+^r=7^b76fKYGL(F64p^^1^R@#^7_`2{Z$Oh{wYQoZ(I=3C6^MYx z+l(7P&GJCasxV4UT2V>~(j&ok9fmi`N*HSH2HqkF)UF0Yyo{$55p|!854=Tp()Sxn zo-%+jeH|wbCj^63v&Dk^Ij#daqIbNkMz~UAEe`jH1DVdv^IE#&9~tWTlmQUhqA>SM zQqyvQl1Sw~;3Bg+fdvEg5~Pwyqa22o(tG8#MSz9`fP+ZGL>3%^1qmypHGicun5Nj{ z-OvTNK5lZFG}(I#{9hLa!w3#y{L+9&_PL)sPUJV};P({djX1&d=n|N)z{UDFf>Ll5 z4>uxMhJ#!#Lcr*d6IIrA=<)7w6s3?T0e$Y6%TnwH=x?)@1?qfBgG_a7iv11j`FOYF zHZAm0dH9U2GvOD}5wjTYxn(2bCA#>bSCy0QuP3*p%fCnjS=z9Dk-4}_Nmde%`qH_qnc!{6P9(Wqe!|HY}ld{N4A9^#A`6zDWS%O1nC(PmY@NCVU_k&|rl-_Xf`)@g-L=2>d6$oS7E zG3^UlrxLzFGg^<;``j!9qw*`#f}q6%;WPIGO;|Xvp?g6MOe6}l1Q~qr+|-JN0`(Te zkcliyh=hdJdxBf4Tsx(ZC~;^$jj~9kGb9brk}~QLU%}y2x+tXq$u_di6cQ6!+k#ZA zhTQCWDa8bQqm`~fLP3ir>?Y%YC*1)oDAZNoBFS**7r1a|$3MfufLhKEH6nrwW-$gN ztI#hk;4|ES7g%J>vHAm_f@~`E+%-Ha#tM$NQZ~a~cyjh<{GtZaR~*g>{Hp`08saoq HSK|L#lKT@=Y( z3^PTE#wC@zF_T-1akuPD?Z4X2+I`;ho%5de{GR9geZTLV=Y1^A*w{q?+|0Eef!79n z1s(u9z{0>r@01DLPNqt($<*SciA7&=Qoh88as~Q1VBqN0CP| zN@k8gLuVj!=;eT=fCC>JIai?_Z^V2J>tQv*`?0BSzk;24>Vd;5?_UA>tHLV~7vvRy z_6WqGfc32p`QE?ra8`Aj`?_d1t@u3b_)!QwHd>`wGr9% z6DC(I84h_4Hzl8r4Jxs3OayYvjTBf=?pVLMgX-=Q&BO$xB?K7?>k5fYAks%@KpGoy=`1fLi@D+0_2n} z<+-mf=>f1hd!!ek<>5%%6tZt=sxtPEcf+{rB#^0Mzo3x}|Co$4pW<+Qb~HPusCsx> zsYk!HM|LqZQ_rN$Py1;VcEPvUqFBB1Z7$xX4VG4a296;ucTNVugr`>Qb#L7@x{1ij z_kvF0WK2SUJh5hy0Ry6OA zbL0y7J6w-L@UF>1+~Im0E3$ONk&WDAO^xmYh%lmTgBAMXp&tB;K}0aIWUcI;lz-SS zO|Cv-$2lnd3r-doH3sMTAyb7?TF)%VxAx+Iq|238nR5K{yiG$gSbr-Gn%GtO0Jgbl z9}SgmqOf>u{GvRSb93UV?rIke;<5S2 zxTIrC(i6FgI-_dr5g9y9jVn%tx}M(DyL2hpr@`)J#lYJTL|OA6gXVFgdwqhDjQBOm z9-Mqr%I%t%KxrUc-K08AgRA`+q{{Ph8OL>!%Ifz9X2>GXuMj}VN?&kzU=OhY$|y9+JexcRcuA1QbSj{ zz+I=yg%6-aniW1W`nhn}C_}kwYIG5Btx&R1;{uMfEiGNaJ2IlwPSsE`R+%K~Mmt7| zLPS?(K=e`4#Q3TC0(XTS_n3DEg$uiy)4(=I&EDwQsM+MiB2GtomhD~0e2~A?9k2Xf z6_u{m)2SsTigko~x+9tfr9;9cc?ky4)hTR4|5)2`zgM;@<}MOt3ZG?k(4Wb&r5&xmZp^mm`vtmw+wA#~ZQpA2z^I6I?q9+uUH_EypP-kBe}p?}SJ^SnNlzXwjJ$EyC*x(9pkdPq-*8$h%Teb$ zAyeaQ06;3|CnNi3=YagTb9}OlpVRj+j<%wte9F6=5_H2O%Ke|-4C65A72WSNG`2e* zUD8tZ;Jq|7!ETg2Lh5HY*O@q4GLczOFm9>WcKv)*HHQN4?txva$v}9@=-agc&<-_s zJ5M`*rR8OKYT)wFSnD3oxPgdPUy_NA`TGfwj#D4828sV(6 zxjm|}>KZilgM=4PAV<1FeY}>iV{iKtfND!)ljI2_4yVdn5(CYiuZ4`F_usNAupQ>wFKaeJr6zRGCJTF~^uoL(p zs_&d#V)jW`vbXD!=WM4viB@+FoN?vA87EI*naW_f+Y_57swE0r;_hu=o z1~xLv3`geJltaL?Q1hu|_DwaaYN_}3v^DHZ7tI{A{tT;^SGLoDXWO1mmyY`1vMjai z4)ED&SMOTEF{n$)jy%=-^;Bs@V)LtfhF{?nCI}}3MrQT7^wl42P69Wfd#h#C9*Vz6 z%oFN;Oc;gH>jlc!P__pf%xf<9uIxEqLD}dd33deM+f9bqKIUr;rVPm#Ugx*><_93N zVm7}Y^SsNumM{oZ3(Csutyof0Esere|=1nmFwt3~14Mf^$lfVa`2B z_#bif$IRnetKqqjA6hl;jh}w12-;!mLB2gc`O$wx%?OVPuo1XCK5DKlOJJ_bNJ)f{{+o6@(cg~ diff --git a/kryonet/lib/objenesis-1.2.jar b/kryonet/lib/objenesis-1.2.jar deleted file mode 100644 index 45cb6416838ba263335373cd93d507cccfcdefe6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36034 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�vbna5Pgjz%Sr1t^Y@@PI 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 Sp`aKc|G;RFs3XtY-~JyEsh!CRR zAaFPYbkJ%>HaavqqD^$n{19k?pcHMIiAXALXIiJ&Cmjv4T+=fkx^YM}Sf?w)%j@3U zT%cEf2RPeEKoCidpc&cy$D!i${7tvjxL@fX>8~s+G!{X|Ihk1^808rRsSN1n>H-S?RcLuADTk} z_6PXZ6%zLzU_}`S$Y(SH5j0>Rpzyz!;{OvTdKqFmh|=#`?I3k;^q$XSt!mGqzSgzZ2+^|n|m^dJ`X(YdWm{%340-)7j%4D zFJ+^G-E^4_@-nFCtZ91e?cF=IZI-f|i2j*zd;1)g$^Zn2vBi(pgO^3KEi%FA8|kCM zdB#avfRpnGcUktaKI-ORM^IF{;ti2BG<%JqZ-|oI)w=uDTdfA< z8;;cbZGNA3j*u|7uZ0#*(+{~FbZx{mw#QGqZ{w!tOBnvbCp?GYUnMmt=*8D1?#=`M zP+QO?pGH6=O3;mKCOpi|FKxQ(E8BgLr{Y0Ph0@&$`ncMj^4) z6xEN|06}F^E*u15`+{1qBOS?yEmE;>CNTjGSX>oYR+HO%A52^P5y(CfoAicGK`-4Q zHDV5VY=kQc-NI7K9Px#iFF4=W=}*Eikimso$mAiypy5P{UkS$J@1TI;#JF0z=%Z3z zx-M2RihYtpPpN1WdLhbNix9}v)su!$msv#1W0TvE^i_vF-=snp_dkf?SG=RYzz{9c z@bHbBP)7A8E(upwwsbAZ$hPyT*QeyUVHYxZ#oC&}VFU_Je(C3-3@Kc&jXt~y{L{Jk zd#el8K!JeHApfV%#q@vZT;f({wx<8yzW%*uHETins;E7E$tRL?oLxK-N@_L{N?O5$ zR;UdSy9OIu1sWqMGTJS&D<)zQ1<9e$Mf+_k21D_W;3EK;Vjx>2{YLkbs9B8fEu2 z9xR0~GOF#fX!r`J<@e|dV!*Y1DyHp89A07fJrE>+TED6Ov9p&44?EC{`+_epwl7is zE=BF1TJ6UUvLedPTu+ptC1NgwE@Z{atOd4)OFBjm6CSfrtk|d&_Jf3K5td~Haw3so zjhFLiOgK$Ufaj5-)D|v&*yzqJpH{a4lQgQo0h2hYpn+3&9gnDUB4wD7Yu}M8hr}+~ z41mOrO?65MQ8Gsv7z47kKs_r2pfW%QYoJUX#?*>V5v%DV52SMr6l2Fe>7%_&xw~~P zABM+&uzdB7rj1Pzi%7+bJ$u6#YPhR2}3SlnSj)UM4r^3@+L@yRoc#pmhz0 z^hQ^qf3CGhWqtpm6L~%$l-FxU+|NCJ@*G-WN-i(6(>}6?QQ%t!O^V>=n#~5aHEDJJj zhAZW;HiQ)c-kHlIW7P0&S|5y9RMuD5N_giAp6H9!ylll&?0^*v2GvHwXlx!q-|MK7 z*a6B`%JvC4tz)aLeo#geBL`F6(rR8I&&~*Hmz$3A3MZ!zCH*Ar> z;~Qy(IUUpo_SRdA+_^jA;Uue=82uLV!

    t2S#1=oA81D*b&E{dVIXK+=#YR&}9kg z-Z+tm78UQWLIls1F9~M8nkR5LEX1mKmyJCGLK3Sh>a28_4^=f!p|DSD7ru1<3T-4~ z^ww{G9(bt6Y0u#YF;GfL87#exg`lKh+q`|}8-sg}dg>g&W@qmWv^^sAkE`vUE5>sk zd!PFm;A%(t5}K}&!W{K&80#90)c~!0moP6hKZHmSAqG+c;R8?#c$a=#cnwwumUtVC zjs7y3!=DO@f0qG_MrQA?7e51=iFlvwTnk&X%45J~*KLLJ$4Fd`Ki=bW+(Ck_trg4J-S9`z)SG&)K zqgOazd!`KcXO|An!C5HnmPU_0ipJEbn6@Tyc&VXH9J{waZ?QQVN?T&Z%9U>`f8Ki? zaRaBhjg%BtTJpsGWw7UHoAjtG(vT5x$}W1?!o4>zL^m$e-5#LJHW;Mg3lQg+pJwk6 z1MG~=I}~rQo)>Pgbp+EdoXWp;By4uMKycTtFYekcmSy=@&g(f}{3+~Y>j-DJa%I>m zo&Rjo0zi=BIwLQ}5Oit>&sRJ*xp}I|k4xB_Cl+vfUBT&+$x8chiRRwrj?|I<>`vH# z;^9ANOeD0-o{NpILJ@cCCLfV#Y87)Q^ZgEOu(GuL#iBt0k58wk*WW*RV%>jA^KqRi_LnbFv_hDuy7RNbca(vZl>E@}Afb zqgCvRLpbE5y2A*va_tsJ!lKTmLc!fu8lKe+Ak5yN4gMMJzfoQqSNTh}knieB-|G~1 zTqZ$5oP>5(A>>CrNq2z$#E^@i@hLfj3+<`dNT{>dvORs(_973jUBPdbv{iy6>!4o7 zf~wB}{8~Noi1~yiU14Xn?i#jk1xItD0A`xG!-E$@*vCj3rpa`5WD-@3rx3pw-{(mc ztcER&{XxG_3Ff6sD{E$ZH8;GgZ9@Q4v46L&*Hp9J<7fn(!oKMi+ zoUcJ!a~$5GRNe#tjz0$^}L}KIYsF@-~eYCf%swM}WgC>i~&i>M-`4D=1 z?7>k`oG~RyGA&wPm0F}EO$FEC_Ypw3%~#2ay0rAYo2K$b^x>uWz(<8Mw(A3Ri%d$D zoC<~RXwa2Sh2-`x;_nRCb3~-+61YPl-4(|cAJTww_ilmLK-Wmu#wL^ zdJ(;TRS$6qai3HRv_FZ{7!{h^_{VaWwMFOQg&iq9jwY2xzFk8e1X$oaWehQIfSgK6r@h2UW!+S}>QlkkGI;S5{q>>0%SFjV zhHn*3=2mLhnI~Vs`f(n7)}!0ftz;#^od7Ed<;%ImHDjI*q~jF?KhT%tV#FD!;!3sMmMB2$~7Vlbv@fE{7oUGIaXz5>jkk}sk1_Js_?+-t! z8LzULO)W9NCywLygCM6a64*^uN+UX+ueN|EgYYDE>hRRJ&;t#dfA#g?#p>M>xh`o5 znC1(FIr8DkFWU}u&+pw84qCl}y@nzc#YG6p$jUO( zghq}()ydMjun=%}f%M0|N(O12#dUyc2!_^9=k|t0+Bft+o4+( z{;+YCnYA_*{>Z&5k1C$pOS8Mr75?b6<=zNi3QMyG2o?Uo0jiHto>EHz1CzWgJkQS- zCQ!#(bwntPF*|{>?q2F{UsVJCV?8Krmx0;XE)ix^Gl{IF!x_t=fKg7mVf)HS>_B~E zmv!$s2fnH;#|iU*C0A6*Y8U^~&{k&z4<4x=cS08zxfF7n3$JcY=-VvnE^m=eHRxb-7#oO@cWvViIevn|Kw;oWo9xps7tqtnJFWgfG&C^#yywXpn z=+6ZQS?ZOY(_t9ZP{Ps0ix{+m=v#*zf(`Shc66WZ zp$Ytm2|Yw6x9lkI@^5FYII#3Ml{VV}VbFR=n>PI@6(=rq7fS5`QO+2uxjsjSLoH7) ze2g2}%v*Nt8z}8tyi{Z(EHe`>uD#L%$T`WTMb2z_6r>{Q5KH(0S7`1sFdE8VDX#45 z^V(G{kO^541DIX-@dvvG-?qRRy#2QUV9UNqu4^vQ%|b;tK!sByz8*F0A8fmRy+EbnRRjvn+xu6VLaohk9!bE|YvZg7W& z=a%P{g}LjHW=&wd%WscDADcgdzx6D3)hgDzr1DJb8oL!=nVRe5w9s?vS;=3(PwijT zmR~e6a|lHCFnQ9Nw=}V~S0SS`A0uadD014#&x>6^=dcGa$#VOEHu^A1ILE#>qDe5W znAK;FPpMQKw2yJAwb62|PDkZZfT>eCiaIWAUHf(PzW`0WWy#8ed2Np&fX}*g z+Ld9S+_fk>m#)?DJ0dJxxwwB!TI=2N+)||bRf&EI^C%|$*jB(zR>LhOpk`2|x)o+E zrQ0kjAoonUR2)`8RAW7d^GIIIy`|`NKwZ0C)k>+>U0@wksqL26qgqamU18?5#M~`s zP@~!|O}4dfVaL&M@F%eBu?6JpqF*V=ohtF?d6UxftjyZ$@+a?3@FE#;*q!=xd{#VY z8v5ccrKcL6nd7gpdf^sl>bw%qMhK;Nvy1Q_f&OtAmI(Sq8eEv4 za14w3UD5*kY)N1^a`0U2=T_)I39=96$#StRmB43df@uN)(ga~|= z@f2m@Z{)M>-#_kPrO9Utl*l1;06q@!bhNDk{hgF4FFVVW8K#_6`xfv5Dz8?%6hjIW z0v9i9l<${D=dPzTsBh}nL146+<)G7HyQt_@%0Wr6{mAH5>OsyTbA3T9$-9;!yJr0v z(u|vkO%1!WWMfRAlbVj~Fozq*m_a)VF}ts@h8KZA+d;x$CM^7>K;JpQU1`7{Og~;w zz;8-ncV{vCz72r_M&w_zfj_{0ywHH(tibL%VD;@V{rnzx{Kg30_mQ9QKUBS-pnpyX zR_TFdo;S=L*-I7=H7zDaX3e-{O<5=pW6c!FXbNd43v@2aafMb2;LZbI7=DSY*=48& z5V_I>bSK2-`%tB|%+--H_8|Qr>Ba#&fC9&you9qJi9Q=Qj5dksH={>FHgGR>T<>($ zEl?R!u37tJWDL4r7O}{Vz|`(cATrQf=9Y=5R$+9_i*TdZpF>kRT{l~SFWovj9T5JS z(x+`b>w-(K>9Y|ay+P-`x}{YV5Ww#Gy}aS|z?0MRh?!{o{m91G_7iCrIUE+&%P~m2 zZ)#kS9S@9RG_Qaan}icQrVu0=AS|2DlrC!GM2xn_Dvt@GIt4W_pN$!PM4At3bH=Hw zfKr#Ri91QYuar6DWW_w1PxS)BRAousWAdq>I>T6QsHv(fkU6u#pA@zj0h_L+I>neF z*vuMgPGR?vmkG!{CT9n^Go>r+#l|1rpyTriUGz#2Qja>1%fkMPxu#^m5O9Iy9>==1kH*wGT zl6KtHoRg)~(YJtw1YRrF>N0GS%p7kf+H~r!k|u|#(#@RFIyd$)ZLcA#on^!}3^p`+ zO?J&Am1QwwDHQyRwsAG3hEeCDp;7~i!UtFWiPHRvX^J_iN~xcH#NHgY(|PSmxe9(; zHg|Yh-i{Cc#D4MlB5B1jRJe94r9Mv@f9jIXODYklh2lrLYqE>hs7Je3of04Osi|yn zC}pWwdjHyGL=$Fn6R4Z7=d#KVbY&8B6%^jzunZO*{MKSP1Ql0J(TzLoGv1%sXW*Et zSJ_M8PDG)OP@zs(ZoAUj5jL+k-GS-{;@m%4=d@1uX}w5tMj>vn!9Tz}LlKTRPKCSV zF!o4ha(nkNpXu}loNiZOUu3BA=u2;L?_-TJvT910JHn0^xbGZc9~K{*BJK0pnsS?Q zG+ju(w}$3#%)Qg?+!yU0c+Ly?TzNh-uK&`@S^Pyi3mC2(pGntDNOsIxXWNv4+vXtM zC=`kU!GaILr18daY%{r&Nn!Ht9gEI&i>zvq>eMz4P-mJim_L#R-6tH_kZ9wVDy zN0$SnFqfsZ*AJR_+2mzB(4XU#XwQaexdRa(Ua5`f_Uq7<&?7LTLZo#XNbRZ;*@ch! z7STL9v(FOQgxyyw<2Y?Xx<;RrrIzae+30AGVhjtKOfi$wvYB*_H!1eW0n8~afFSR5 zRs=vM%NW-Kc0;5r`=>YF=h*xH9LR-XFw9XmYrc|xd@tej+F3WfKX-vd6^nICrl9DRQ8qNaD+_#IOzN}eK{pLo zEcz`M7beSGt7Pv|AJXbm7~1KVbuRw7X40dANnwL>SeX47=eH%axDFHIU?*2Da@?sm zM;&Xw^DMsjOWj7M2u?C4FCMudGQ6TS_2zewA2;BfKOL4?;#Us#A7TII@gr>D1}o~K zTsUCF6%;+gdEOd348A8k+l)K0_e1e&eCHeP9~RaiuL+p~4G0LH_J3kwng0(gtelyv zrNe)sVXHM^d~wa1zw>f3E=_r)+$pwLe@Nk2>#*03l&~ht=~yL}6p>m@raqKZ516w^ zp>uQ^c`UMng$R41A|XL9?EfG^oI5Wz=17s$5fVZ?+`d5biiB1RtPCpDC-`}iwML$0 z>a+LO`07{dGUL&6|2cb*b%pYW-xIVCdHqzF=&6G-L`Td+hu$O4tpK!@r+zl#lb*9) zV;Il)Bm0#9k1%`>;ZV=%K+a2IBodSVT12PrjbSZ{N2eo#xL$gck6s&s0n(NR*q@(< zzH(DN*F(KLPQB^)uKPH`Pr&njDw4eU`|**@Yih%%R zQq@XMa`YV~r{c)Up$8)j{C*FyXTEtp>W)#-XTN#Antg1vl}JDy;spWcZ4nF&D0tj1 zmD3==_(^1*k1q9_sN)-T^4ob&;>BV9Q*)Sw+P`pL3vIUCQ-2tST3_`xF*2{hQ)_ru z^+R`_3;Ig&wJ*Vv}%lIiZa;NeoII>mk zLuoiq^+Rj;yW%A(vbSQj)VMdY7s}tD#lA})@Idb&VGHBwhV^eExyZ~Y}i-6S#2OQk0 zafWkDf+2CgF&E9iyrVOk&|QiL;^By9s}!XroW%4~{}=r>-en{s83pbZ{yV3_M-MFB zMJGM8$oN}oHH*~|i&Th9qiwF$2gFjzQ|5T8WovuV?&Gwo%C;~kAB-P4tT1z4O0(I( z`D2O&*z#ZyPKgnXX=4FRJ*`o*!*N7)fo4EV#;^KVM@pm%scXoQ>CCn@l)X+clux!6 zx=InE#f3xYA@BpDMlRayy>U}|^bR)Nh)UMW{zvSZ{$_0cz)7|VG7O`y!!^2uUu!tg zRk6?TPg(PanK?u*gjlg+P%b3~O-78kXY20Ym;lf;`*k3^_y}kH*(`6z&D4Xz()IYsN|^TCuH$r zpLs4xgxS zPzxdXpxdyf9XpN4d7IZKISgAXEL!sW{ZeF~j(*Z?;9|$T$`#ZAfHlP{Vn;nM4^`P* zGj>{s#L>M@-!eh~10)u(K(J{FLAO$+NtFRR zfaR^6XRa_Ym}MFT!f_PA*Xc%ckH(#_yqguXa8KyXiDmR^=sc3nQ^mpqXbyCMZ2G*t@VM$=i7LL*P7+fVDQymwX6-yT_dm? z;c5Gg2wB>kw@W#?v?_6wKc-bYn<(rD5mdYKze?JP{~!mNGGf8shJ$rxPEW&8un!Gx z1Q#Mo_S?q>@-Aaw;xZl(ZIlgA>;q8y51KDlA z|6;64^?TB&HlOARx=pnVQZX>19YTX77$hKt8>nC`T_0TGDYr!`L%nrD*6H!N=Ds+r65UT!Ry z5gYn(9%>acAVr2lR54Q3`Aa(vhzBEe#zRKjPaPWaTIV$}Zb)e%&-94cc#QRkEL#X4 z$}y8~yHp-V(bDa#6ZYWAevPm%su5l|q=nhK8LI!@>>niyo{#7Htj;b=K-2ndrpW#{ zY{DbzS?_gcg(*w~1o3^#0A({m@Wfj|c&ifpZls@bC&^2VW|AMwOQbvP7c2HK);a8e zlpAr|@sJ7@|B5h2O-)mw?5J8wxFuj$+Ryi$EcFMn#G@)%UELSuE!qY zHQ7&58XlI%@}YZWBw>*Y-r@vPm5&1qqfw7)3Om@{bsus|nH|F`JscVT1P~3k8LUte z*O~na9j8;Qw(R8QT&{uNFI-P4M^L75OI_Ra#CZ4mu<&CLIqyrjW$_W@I^@QvO~HB! z1l5p;+DkV`Y^ohNFB6zCj~}lD`r2WrwDH%m9EM)B?qjb5QY;e*1Es$zjoMwTpL7xKa}W7vLuH4A#O8TC<7;Qr=9 zz^>ZQz4Zq-CH#Z-SpTv{s$f%KQc+&E*-6!P!ldls1k1}i<(<_8{cI_Dz?ei~+2#QO zWj5{QKIQ|}I>xt^sJQO;*t4N*E&3(u3}R(46)nphnfWr*I7I6Y`zAmuZ5sJP|#o?H5}kX_<|D}1qNzPBlXMjXM9 z-rh$Z_j|Nd9ynb@p=u!b2sN045+C^G@)*+4-5srtL$^Oj@|I4Usj{z@o&l?4X=blP zsP58X=<%!TOMo$!I~URh^a0IDR)n4E{+l*GDpvcNhjS}|8Fur=ix2(snlT+y&$wvC zywyaLosla#>4FG~W)Onf)?bYlF#)km6rsnpf*$~D8f##T)}zhBO_0qe$Ihs%v8&e&`On^LWgF}M|PVHP@AkT zrB4&|$(}U9W02$60lxYkMM<9p+?C@!Vr;)>_9xNy8&H;2;Qql=WL}x_0;mPv!9gmb zfyLumDG`6vCjqq&fxom|tKH_z(emsS$|nExmApL!7w4)IpiQ2Ve@Zd@!G3ZbkKG6l zMK)^A1Z6#a&ID(CIAc={%~j``eSt8@#6WRZ%0}Lajm7=%Pi0 zny^BNhKaYSaX5s81*tD|aN))^1EQIP=^lKIb9-F0vk~?2JS9Ihv(*U`?5j(Rt_VNq z+E_LJ&|0cbuYd<`nk+AAnmH;Pi3e;N-VwzCTvv?1U^quIFj;>nZ^32Lh)i<#MD?m! zn!f~(z0v4WgHM{XbPg~=Y5}C%$5i&X!>F_StCnWJyg`(A6N?{;dLc?BC+Lhghq?#j2E9m@8Q!YKCT$zu*D#B)B_&IotcSl}J=x?{FSZy=oQ;)U&!_y^cYLNpNCt}k<4 zhVQDd+p6^As(8!`rQOhI84AW+Xj{B&h$SNVl`zAVIKh=rx_!TxD+KX_*~22Rs1+`g zcl2l}qu}hWF?TFzcWBWMOxL!$Ev27$v0#|av?KHqc=T>neP*%by4RXyPKP%qKRaam*tPtv9P z>gBktQfh5_6@&ccQ18XfFWF5^07)A7(RAM(|Rv6+%la6Vwbl0i&!+Z-@Z5kao z&r;9(l?z0^dfZ`K3jj{aPsE*g{h=QVS)8OAMzS@-MqXI>$v8~(90x>RD6wN%PO|K` zc2V$=do>ES`rI9!cG{Y|$4UhsFGkpxn;9Z4PgOwBFxEkCyG-fpZ)vkMN z#d#*@$RFbE*u84@eSea(ZpkHn>r7bO`ovabRWtPKeEplQkh$xyrwtAW$Orp>`Y!yx z=n6#~>|I=)-Ar8n{$2hziQ&I%bgDJpJ#dE6zD5$eY`l`jq%TbuL&l(_OxGH)QG^q& z1Z{1NiD^jPZE=(0AFb_b6WoWb(MEZZA&VLE1*w{(sQi?lfyu0d+@1QV9&SW70`gBD z9vIHf&T{mi##7f3*qc3 zF_`X+2?RRt4I%Zu6TjDh0yyvZxJtI}VV6uc@AMM?pqn20P1`Sv_QKl8HiaP|gMgX1 z+P#Zx-{2$%=Q}$2+>^t^Uqna$RhAMq;JBjsCZiq#q za%D0{epRn_a95d?uvQ9GA3DQcvWob{*`Gccfh)AxMfYzwl(2@$L2by+{4X2kQS4W!M%3E7y^*NZVx)Uci2)0Hc4l^Q~@H;Oty zeTD2laOA67qtd59_5mwUIzVrc*ybR`8EYV^ImCb^d;v{twH|D~EeWTq*j!be6&7N! zw*uQgUNMn;gVQx#*FNHFcSEqA+gSP1*qP01I!?G#635@c~F=>g0UGTnnCN4(yo8=o!;BS-{BQ3%?}rU`ehlGLS8 zREa#BZk()VGSoU!L#+XVwgH4X;=Q;R#Q_OVi2>@o?Y=xrkc*2{p`tI9=;LFynAvGI z5j{>Jy$LS3_sPoAXkGp+Hah65+bVM^-d?nbZP_DK<>>gCS%8goX-M2Hl!!~)ladXw z2)J}Rm3EWRY_-)D@)Z`l%3D*77FVecLUnnYF^F+7-(ha9OD$vv=zvJq8cY#MxoHKq z-#lsuPqM8#i*oWW?Ns&pNQop+u?<%(8xTF?*K&C}9*&xg#&0snQUM9Q_Tj@25+mwz zCR5c~Bl%ha)Hi`%l@mz^rwQ%?)NM_cZ2_{= zrHg?|A`89>ZHF)17df^wa#Q&Lmk zY?12UCR3fZn%JDZld-y`N+3;nqAO^aaVW?xXJ zCNPnfwsV}f(*Yhp^ z$*_3n4Ggi~jao{-?ke#rd0CS5gBKJE1v0)=he=9bKvqAOsCFiiB5n@e8wAbCZs2jQ zHQW&a{Ew?+I251}07X;urYK<;lyBMcTVJ&FmTl(F=^>Ww^!@Y$NF}5O*&BKze~(?3 zBnV45uUPXOwU?{m=e9u&m@SUhE?K!vESLuuIw@c?Na5D9$x`fhJu&uf3KGe2Oe}LF zROqfokO#zpsSYdSbwgWoapHN2_4)2-!zdk1&+H=+wIY{0U18J#sLv#Ka5zj`cqPvRea;0x^u8io4CtsL(A=ceGVXd64Q0kYXfOvad*?{w5yIk%`d0tNGHcK2UJ)#G& z!afwQ#Df=asD>kw!#?u%z~VzsZ?wlP?NYD~6@n7|>+?gvUzY*|@z5hof Date: Mon, 11 Nov 2013 10:09:01 +0100 Subject: [PATCH 4/9] Fix indentation in pom.xml --- kryonet/pom.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/kryonet/pom.xml b/kryonet/pom.xml index 60a47d2d..883b3a82 100644 --- a/kryonet/pom.xml +++ b/kryonet/pom.xml @@ -31,19 +31,19 @@ 8aca6c467d2a350434f08dd0dce4ba9a - - download-minlog - validate - - wget - - - https://minlog.googlecode.com/files/minlog-1.2.zip - true - 32552fea7ca0189dde2d15e6948acecb - - - + + download-minlog + validate + + wget + + + https://minlog.googlecode.com/files/minlog-1.2.zip + true + 32552fea7ca0189dde2d15e6948acecb + + + maven-install-plugin From c1236347c05c898477425df11864f0f775273738 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Mon, 11 Nov 2013 10:11:54 +0100 Subject: [PATCH 5/9] Add Maven plugins for sours and javadoc JAR generation Also add sample distributionManagement section with repository and snapshotRepository sub-sections for automatically deploying all JARs to a Nexus or in the future to Maven Central (edit URLs in order to adapt to your personal needs). --- kryonet/pom.xml | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/kryonet/pom.xml b/kryonet/pom.xml index 883b3a82..44495e38 100644 --- a/kryonet/pom.xml +++ b/kryonet/pom.xml @@ -83,6 +83,34 @@ + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + deploy + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + deploy + + jar + + + + @@ -119,4 +147,25 @@ test + + + + my-domain.com + + scp://my-domain.com/var/www/kryonet/ + + + + releases + + http://my-domain.com/nexus/content/repositories/releases + + + + snapshots + + http://my-domain.com/nexus/content/repositories/snapshots + + + From 3432ad55c03a3767f88d96b7df4ca00f1ef54fb1 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Tue, 12 Nov 2013 16:30:37 +0100 Subject: [PATCH 6/9] Ignore IntelliJ IDEA project files (can be auto-generated by Maven) --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 1f01d76e..ce341e4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ kryonet/bin/ kryonet/target/ +.idea/ +*.iml From 1ea36121d591ec417f8e3436479e4786c6287e77 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Thu, 14 Nov 2013 13:08:32 +0100 Subject: [PATCH 7/9] Move folders to standard Maven layout: src main java test java --- .../kryonet/examples/chat/ChatClient.java | 494 +++---- .../kryonet/examples/chat/ChatServer.java | 256 ++-- .../kryonet/examples/chat/Network.java | 62 +- .../examples/chatrmi/ChatRmiClient.java | 468 +++---- .../examples/chatrmi/ChatRmiServer.java | 258 ++-- .../kryonet/examples/chatrmi/IChatFrame.java | 18 +- .../kryonet/examples/chatrmi/IPlayer.java | 18 +- .../kryonet/examples/chatrmi/Network.java | 54 +- .../kryonet/examples/position/Character.java | 16 +- .../kryonet/examples/position/Network.java | 102 +- .../examples/position/PositionClient.java | 326 ++--- .../examples/position/PositionServer.java | 484 +++---- .../com/esotericsoftware/kryonet/Client.java | 984 ++++++------- .../esotericsoftware/kryonet/Connection.java | 624 ++++----- .../esotericsoftware/kryonet/EndPoint.java | 86 +- .../kryonet/FrameworkMessage.java | 70 +- .../kryonet/JsonSerialization.java | 164 +-- .../kryonet/KryoNetException.java | 40 +- .../kryonet/KryoSerialization.java | 144 +- .../esotericsoftware/kryonet/Listener.java | 340 ++--- .../kryonet/Serialization.java | 40 +- .../com/esotericsoftware/kryonet/Server.java | 1120 +++++++-------- .../kryonet/TcpConnection.java | 488 +++---- .../kryonet/UdpConnection.java | 274 ++-- .../kryonet/rmi/ObjectSpace.java | 1248 ++++++++--------- .../kryonet/rmi/RemoteObject.java | 116 +- .../kryonet/rmi/TimeoutException.java | 48 +- .../kryonet/util/InputStreamSender.java | 76 +- .../kryonet/util/TcpIdleSender.java | 58 +- .../esotericsoftware/kryonet/BufferTest.java | 182 +-- .../kryonet/DiscoverHostTest.java | 82 +- .../kryonet/InputStreamSenderTest.java | 134 +- .../esotericsoftware/kryonet/JsonTest.java | 330 ++--- .../kryonet/KryoNetTestCase.java | 182 +-- .../kryonet/MultipleServerTest.java | 128 +- .../kryonet/MultipleThreadTest.java | 156 +-- .../kryonet/PingPongTest.java | 422 +++--- .../esotericsoftware/kryonet/PingTest.java | 70 +- .../kryonet/ReconnectTest.java | 110 +- .../esotericsoftware/kryonet/ReuseTest.java | 120 +- .../kryonet/UnregisteredClassTest.java | 344 ++--- .../kryonet/compress/DeflateTest.java | 142 +- .../esotericsoftware/kryonet/rmi/RmiTest.java | 410 +++--- 43 files changed, 5644 insertions(+), 5644 deletions(-) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/chat/ChatClient.java (96%) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/chat/ChatServer.java (97%) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/chat/Network.java (96%) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java (96%) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java (97%) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java (96%) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java (96%) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/chatrmi/Network.java (97%) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/position/Character.java (95%) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/position/Network.java (95%) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/position/PositionClient.java (96%) rename kryonet/{examples => src/examples/java}/com/esotericsoftware/kryonet/examples/position/PositionServer.java (96%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/Client.java (97%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/Connection.java (97%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/EndPoint.java (97%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/FrameworkMessage.java (97%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/JsonSerialization.java (97%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/KryoNetException.java (94%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/KryoSerialization.java (96%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/Listener.java (97%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/Serialization.java (96%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/Server.java (97%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/TcpConnection.java (97%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/UdpConnection.java (96%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/rmi/ObjectSpace.java (97%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/rmi/RemoteObject.java (98%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/rmi/TimeoutException.java (96%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/util/InputStreamSender.java (95%) rename kryonet/src/{ => main/java}/com/esotericsoftware/kryonet/util/TcpIdleSender.java (96%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/BufferTest.java (96%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/DiscoverHostTest.java (95%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/InputStreamSenderTest.java (96%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/JsonTest.java (97%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/KryoNetTestCase.java (96%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/MultipleServerTest.java (96%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/MultipleThreadTest.java (96%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/PingPongTest.java (97%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/PingTest.java (96%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/ReconnectTest.java (96%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/ReuseTest.java (96%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/UnregisteredClassTest.java (97%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/compress/DeflateTest.java (96%) rename kryonet/{test => src/test/java}/com/esotericsoftware/kryonet/rmi/RmiTest.java (96%) diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/chat/ChatClient.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatClient.java similarity index 96% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/chat/ChatClient.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatClient.java index f972f6e5..f2edfdd4 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/chat/ChatClient.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatClient.java @@ -1,247 +1,247 @@ - -package com.esotericsoftware.kryonet.examples.chat; - -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Container; -import java.awt.EventQueue; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.GridLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.IOException; - -import javax.swing.DefaultListModel; -import javax.swing.DefaultListSelectionModel; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JTextField; - -import com.esotericsoftware.kryonet.Client; -import com.esotericsoftware.kryonet.Connection; -import com.esotericsoftware.kryonet.Listener; -import com.esotericsoftware.kryonet.examples.chat.Network.ChatMessage; -import com.esotericsoftware.kryonet.examples.chat.Network.RegisterName; -import com.esotericsoftware.kryonet.examples.chat.Network.UpdateNames; -import com.esotericsoftware.minlog.Log; - -public class ChatClient { - ChatFrame chatFrame; - Client client; - String name; - - public ChatClient () { - client = new Client(); - client.start(); - - // For consistency, the classes to be sent over the network are - // registered by the same method for both the client and server. - Network.register(client); - - client.addListener(new Listener() { - public void connected (Connection connection) { - RegisterName registerName = new RegisterName(); - registerName.name = name; - client.sendTCP(registerName); - } - - public void received (Connection connection, Object object) { - if (object instanceof UpdateNames) { - UpdateNames updateNames = (UpdateNames)object; - chatFrame.setNames(updateNames.names); - return; - } - - if (object instanceof ChatMessage) { - ChatMessage chatMessage = (ChatMessage)object; - chatFrame.addMessage(chatMessage.text); - return; - } - } - - public void disconnected (Connection connection) { - EventQueue.invokeLater(new Runnable() { - public void run () { - // Closing the frame calls the close listener which will stop the client's update thread. - chatFrame.dispose(); - } - }); - } - }); - - // Request the host from the user. - String input = (String)JOptionPane.showInputDialog(null, "Host:", "Connect to chat server", JOptionPane.QUESTION_MESSAGE, - null, null, "localhost"); - if (input == null || input.trim().length() == 0) System.exit(1); - final String host = input.trim(); - - // Request the user's name. - input = (String)JOptionPane.showInputDialog(null, "Name:", "Connect to chat server", JOptionPane.QUESTION_MESSAGE, null, - null, "Test"); - if (input == null || input.trim().length() == 0) System.exit(1); - name = input.trim(); - - // All the ugly Swing stuff is hidden in ChatFrame so it doesn't clutter the KryoNet example code. - chatFrame = new ChatFrame(host); - // This listener is called when the send button is clicked. - chatFrame.setSendListener(new Runnable() { - public void run () { - ChatMessage chatMessage = new ChatMessage(); - chatMessage.text = chatFrame.getSendText(); - client.sendTCP(chatMessage); - } - }); - // This listener is called when the chat window is closed. - chatFrame.setCloseListener(new Runnable() { - public void run () { - client.stop(); - } - }); - chatFrame.setVisible(true); - - // We'll do the connect on a new thread so the ChatFrame can show a progress bar. - // Connecting to localhost is usually so fast you won't see the progress bar. - new Thread("Connect") { - public void run () { - try { - client.connect(5000, host, Network.port); - // Server communication after connection can go here, or in Listener#connected(). - } catch (IOException ex) { - ex.printStackTrace(); - System.exit(1); - } - } - }.start(); - } - - static private class ChatFrame extends JFrame { - CardLayout cardLayout; - JProgressBar progressBar; - JList messageList; - JTextField sendText; - JButton sendButton; - JList nameList; - - public ChatFrame (String host) { - super("Chat Client"); - setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - setSize(640, 200); - setLocationRelativeTo(null); - - Container contentPane = getContentPane(); - cardLayout = new CardLayout(); - contentPane.setLayout(cardLayout); - { - JPanel panel = new JPanel(new BorderLayout()); - contentPane.add(panel, "progress"); - panel.add(new JLabel("Connecting to " + host + "...")); - { - panel.add(progressBar = new JProgressBar(), BorderLayout.SOUTH); - progressBar.setIndeterminate(true); - } - } - { - JPanel panel = new JPanel(new BorderLayout()); - contentPane.add(panel, "chat"); - { - JPanel topPanel = new JPanel(new GridLayout(1, 2)); - panel.add(topPanel); - { - topPanel.add(new JScrollPane(messageList = new JList())); - messageList.setModel(new DefaultListModel()); - } - { - topPanel.add(new JScrollPane(nameList = new JList())); - nameList.setModel(new DefaultListModel()); - } - DefaultListSelectionModel disableSelections = new DefaultListSelectionModel() { - public void setSelectionInterval (int index0, int index1) { - } - }; - messageList.setSelectionModel(disableSelections); - nameList.setSelectionModel(disableSelections); - } - { - JPanel bottomPanel = new JPanel(new GridBagLayout()); - panel.add(bottomPanel, BorderLayout.SOUTH); - bottomPanel.add(sendText = new JTextField(), new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.CENTER, - GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); - bottomPanel.add(sendButton = new JButton("Send"), new GridBagConstraints(1, 0, 1, 1, 0, 0, - GridBagConstraints.CENTER, 0, new Insets(0, 0, 0, 0), 0, 0)); - } - } - - sendText.addActionListener(new ActionListener() { - public void actionPerformed (ActionEvent e) { - sendButton.doClick(); - } - }); - } - - public void setSendListener (final Runnable listener) { - sendButton.addActionListener(new ActionListener() { - public void actionPerformed (ActionEvent evt) { - if (getSendText().length() == 0) return; - listener.run(); - sendText.setText(""); - sendText.requestFocus(); - } - }); - } - - public void setCloseListener (final Runnable listener) { - addWindowListener(new WindowAdapter() { - public void windowClosed (WindowEvent evt) { - listener.run(); - } - - public void windowActivated (WindowEvent evt) { - sendText.requestFocus(); - } - }); - } - - public String getSendText () { - return sendText.getText().trim(); - } - - public void setNames (final String[] names) { - // This listener is run on the client's update thread, which was started by client.start(). - // We must be careful to only interact with Swing components on the Swing event thread. - EventQueue.invokeLater(new Runnable() { - public void run () { - cardLayout.show(getContentPane(), "chat"); - DefaultListModel model = (DefaultListModel)nameList.getModel(); - model.removeAllElements(); - for (String name : names) - model.addElement(name); - } - }); - } - - public void addMessage (final String message) { - EventQueue.invokeLater(new Runnable() { - public void run () { - DefaultListModel model = (DefaultListModel)messageList.getModel(); - model.addElement(message); - messageList.ensureIndexIsVisible(model.size() - 1); - } - }); - } - } - - public static void main (String[] args) { - Log.set(Log.LEVEL_DEBUG); - new ChatClient(); - } -} + +package com.esotericsoftware.kryonet.examples.chat; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Container; +import java.awt.EventQueue; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; + +import javax.swing.DefaultListModel; +import javax.swing.DefaultListSelectionModel; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JTextField; + +import com.esotericsoftware.kryonet.Client; +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.Listener; +import com.esotericsoftware.kryonet.examples.chat.Network.ChatMessage; +import com.esotericsoftware.kryonet.examples.chat.Network.RegisterName; +import com.esotericsoftware.kryonet.examples.chat.Network.UpdateNames; +import com.esotericsoftware.minlog.Log; + +public class ChatClient { + ChatFrame chatFrame; + Client client; + String name; + + public ChatClient () { + client = new Client(); + client.start(); + + // For consistency, the classes to be sent over the network are + // registered by the same method for both the client and server. + Network.register(client); + + client.addListener(new Listener() { + public void connected (Connection connection) { + RegisterName registerName = new RegisterName(); + registerName.name = name; + client.sendTCP(registerName); + } + + public void received (Connection connection, Object object) { + if (object instanceof UpdateNames) { + UpdateNames updateNames = (UpdateNames)object; + chatFrame.setNames(updateNames.names); + return; + } + + if (object instanceof ChatMessage) { + ChatMessage chatMessage = (ChatMessage)object; + chatFrame.addMessage(chatMessage.text); + return; + } + } + + public void disconnected (Connection connection) { + EventQueue.invokeLater(new Runnable() { + public void run () { + // Closing the frame calls the close listener which will stop the client's update thread. + chatFrame.dispose(); + } + }); + } + }); + + // Request the host from the user. + String input = (String)JOptionPane.showInputDialog(null, "Host:", "Connect to chat server", JOptionPane.QUESTION_MESSAGE, + null, null, "localhost"); + if (input == null || input.trim().length() == 0) System.exit(1); + final String host = input.trim(); + + // Request the user's name. + input = (String)JOptionPane.showInputDialog(null, "Name:", "Connect to chat server", JOptionPane.QUESTION_MESSAGE, null, + null, "Test"); + if (input == null || input.trim().length() == 0) System.exit(1); + name = input.trim(); + + // All the ugly Swing stuff is hidden in ChatFrame so it doesn't clutter the KryoNet example code. + chatFrame = new ChatFrame(host); + // This listener is called when the send button is clicked. + chatFrame.setSendListener(new Runnable() { + public void run () { + ChatMessage chatMessage = new ChatMessage(); + chatMessage.text = chatFrame.getSendText(); + client.sendTCP(chatMessage); + } + }); + // This listener is called when the chat window is closed. + chatFrame.setCloseListener(new Runnable() { + public void run () { + client.stop(); + } + }); + chatFrame.setVisible(true); + + // We'll do the connect on a new thread so the ChatFrame can show a progress bar. + // Connecting to localhost is usually so fast you won't see the progress bar. + new Thread("Connect") { + public void run () { + try { + client.connect(5000, host, Network.port); + // Server communication after connection can go here, or in Listener#connected(). + } catch (IOException ex) { + ex.printStackTrace(); + System.exit(1); + } + } + }.start(); + } + + static private class ChatFrame extends JFrame { + CardLayout cardLayout; + JProgressBar progressBar; + JList messageList; + JTextField sendText; + JButton sendButton; + JList nameList; + + public ChatFrame (String host) { + super("Chat Client"); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setSize(640, 200); + setLocationRelativeTo(null); + + Container contentPane = getContentPane(); + cardLayout = new CardLayout(); + contentPane.setLayout(cardLayout); + { + JPanel panel = new JPanel(new BorderLayout()); + contentPane.add(panel, "progress"); + panel.add(new JLabel("Connecting to " + host + "...")); + { + panel.add(progressBar = new JProgressBar(), BorderLayout.SOUTH); + progressBar.setIndeterminate(true); + } + } + { + JPanel panel = new JPanel(new BorderLayout()); + contentPane.add(panel, "chat"); + { + JPanel topPanel = new JPanel(new GridLayout(1, 2)); + panel.add(topPanel); + { + topPanel.add(new JScrollPane(messageList = new JList())); + messageList.setModel(new DefaultListModel()); + } + { + topPanel.add(new JScrollPane(nameList = new JList())); + nameList.setModel(new DefaultListModel()); + } + DefaultListSelectionModel disableSelections = new DefaultListSelectionModel() { + public void setSelectionInterval (int index0, int index1) { + } + }; + messageList.setSelectionModel(disableSelections); + nameList.setSelectionModel(disableSelections); + } + { + JPanel bottomPanel = new JPanel(new GridBagLayout()); + panel.add(bottomPanel, BorderLayout.SOUTH); + bottomPanel.add(sendText = new JTextField(), new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.CENTER, + GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + bottomPanel.add(sendButton = new JButton("Send"), new GridBagConstraints(1, 0, 1, 1, 0, 0, + GridBagConstraints.CENTER, 0, new Insets(0, 0, 0, 0), 0, 0)); + } + } + + sendText.addActionListener(new ActionListener() { + public void actionPerformed (ActionEvent e) { + sendButton.doClick(); + } + }); + } + + public void setSendListener (final Runnable listener) { + sendButton.addActionListener(new ActionListener() { + public void actionPerformed (ActionEvent evt) { + if (getSendText().length() == 0) return; + listener.run(); + sendText.setText(""); + sendText.requestFocus(); + } + }); + } + + public void setCloseListener (final Runnable listener) { + addWindowListener(new WindowAdapter() { + public void windowClosed (WindowEvent evt) { + listener.run(); + } + + public void windowActivated (WindowEvent evt) { + sendText.requestFocus(); + } + }); + } + + public String getSendText () { + return sendText.getText().trim(); + } + + public void setNames (final String[] names) { + // This listener is run on the client's update thread, which was started by client.start(). + // We must be careful to only interact with Swing components on the Swing event thread. + EventQueue.invokeLater(new Runnable() { + public void run () { + cardLayout.show(getContentPane(), "chat"); + DefaultListModel model = (DefaultListModel)nameList.getModel(); + model.removeAllElements(); + for (String name : names) + model.addElement(name); + } + }); + } + + public void addMessage (final String message) { + EventQueue.invokeLater(new Runnable() { + public void run () { + DefaultListModel model = (DefaultListModel)messageList.getModel(); + model.addElement(message); + messageList.ensureIndexIsVisible(model.size() - 1); + } + }); + } + } + + public static void main (String[] args) { + Log.set(Log.LEVEL_DEBUG); + new ChatClient(); + } +} diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/chat/ChatServer.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatServer.java similarity index 97% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/chat/ChatServer.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatServer.java index 677e2f56..fbb57159 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/chat/ChatServer.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatServer.java @@ -1,128 +1,128 @@ - -package com.esotericsoftware.kryonet.examples.chat; - -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.IOException; -import java.util.ArrayList; - -import javax.swing.JFrame; -import javax.swing.JLabel; - -import com.esotericsoftware.kryonet.Connection; -import com.esotericsoftware.kryonet.Listener; -import com.esotericsoftware.kryonet.Server; -import com.esotericsoftware.kryonet.examples.chat.Network.ChatMessage; -import com.esotericsoftware.kryonet.examples.chat.Network.RegisterName; -import com.esotericsoftware.kryonet.examples.chat.Network.UpdateNames; -import com.esotericsoftware.minlog.Log; - -public class ChatServer { - Server server; - - public ChatServer () throws IOException { - server = new Server() { - protected Connection newConnection () { - // By providing our own connection implementation, we can store per - // connection state without a connection ID to state look up. - return new ChatConnection(); - } - }; - - // For consistency, the classes to be sent over the network are - // registered by the same method for both the client and server. - Network.register(server); - - server.addListener(new Listener() { - public void received (Connection c, Object object) { - // We know all connections for this server are actually ChatConnections. - ChatConnection connection = (ChatConnection)c; - - if (object instanceof RegisterName) { - // Ignore the object if a client has already registered a name. This is - // impossible with our client, but a hacker could send messages at any time. - if (connection.name != null) return; - // Ignore the object if the name is invalid. - String name = ((RegisterName)object).name; - if (name == null) return; - name = name.trim(); - if (name.length() == 0) return; - // Store the name on the connection. - connection.name = name; - // Send a "connected" message to everyone except the new client. - ChatMessage chatMessage = new ChatMessage(); - chatMessage.text = name + " connected."; - server.sendToAllExceptTCP(connection.getID(), chatMessage); - // Send everyone a new list of connection names. - updateNames(); - return; - } - - if (object instanceof ChatMessage) { - // Ignore the object if a client tries to chat before registering a name. - if (connection.name == null) return; - ChatMessage chatMessage = (ChatMessage)object; - // Ignore the object if the chat message is invalid. - String message = chatMessage.text; - if (message == null) return; - message = message.trim(); - if (message.length() == 0) return; - // Prepend the connection's name and send to everyone. - chatMessage.text = connection.name + ": " + message; - server.sendToAllTCP(chatMessage); - return; - } - } - - public void disconnected (Connection c) { - ChatConnection connection = (ChatConnection)c; - if (connection.name != null) { - // Announce to everyone that someone (with a registered name) has left. - ChatMessage chatMessage = new ChatMessage(); - chatMessage.text = connection.name + " disconnected."; - server.sendToAllTCP(chatMessage); - updateNames(); - } - } - }); - server.bind(Network.port); - server.start(); - - // Open a window to provide an easy way to stop the server. - JFrame frame = new JFrame("Chat Server"); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - frame.addWindowListener(new WindowAdapter() { - public void windowClosed (WindowEvent evt) { - server.stop(); - } - }); - frame.getContentPane().add(new JLabel("Close to stop the chat server.")); - frame.setSize(320, 200); - frame.setLocationRelativeTo(null); - frame.setVisible(true); - } - - void updateNames () { - // Collect the names for each connection. - Connection[] connections = server.getConnections(); - ArrayList names = new ArrayList(connections.length); - for (int i = connections.length - 1; i >= 0; i--) { - ChatConnection connection = (ChatConnection)connections[i]; - names.add(connection.name); - } - // Send the names to everyone. - UpdateNames updateNames = new UpdateNames(); - updateNames.names = (String[])names.toArray(new String[names.size()]); - server.sendToAllTCP(updateNames); - } - - // This holds per connection state. - static class ChatConnection extends Connection { - public String name; - } - - public static void main (String[] args) throws IOException { - Log.set(Log.LEVEL_DEBUG); - new ChatServer(); - } -} + +package com.esotericsoftware.kryonet.examples.chat; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.util.ArrayList; + +import javax.swing.JFrame; +import javax.swing.JLabel; + +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.Listener; +import com.esotericsoftware.kryonet.Server; +import com.esotericsoftware.kryonet.examples.chat.Network.ChatMessage; +import com.esotericsoftware.kryonet.examples.chat.Network.RegisterName; +import com.esotericsoftware.kryonet.examples.chat.Network.UpdateNames; +import com.esotericsoftware.minlog.Log; + +public class ChatServer { + Server server; + + public ChatServer () throws IOException { + server = new Server() { + protected Connection newConnection () { + // By providing our own connection implementation, we can store per + // connection state without a connection ID to state look up. + return new ChatConnection(); + } + }; + + // For consistency, the classes to be sent over the network are + // registered by the same method for both the client and server. + Network.register(server); + + server.addListener(new Listener() { + public void received (Connection c, Object object) { + // We know all connections for this server are actually ChatConnections. + ChatConnection connection = (ChatConnection)c; + + if (object instanceof RegisterName) { + // Ignore the object if a client has already registered a name. This is + // impossible with our client, but a hacker could send messages at any time. + if (connection.name != null) return; + // Ignore the object if the name is invalid. + String name = ((RegisterName)object).name; + if (name == null) return; + name = name.trim(); + if (name.length() == 0) return; + // Store the name on the connection. + connection.name = name; + // Send a "connected" message to everyone except the new client. + ChatMessage chatMessage = new ChatMessage(); + chatMessage.text = name + " connected."; + server.sendToAllExceptTCP(connection.getID(), chatMessage); + // Send everyone a new list of connection names. + updateNames(); + return; + } + + if (object instanceof ChatMessage) { + // Ignore the object if a client tries to chat before registering a name. + if (connection.name == null) return; + ChatMessage chatMessage = (ChatMessage)object; + // Ignore the object if the chat message is invalid. + String message = chatMessage.text; + if (message == null) return; + message = message.trim(); + if (message.length() == 0) return; + // Prepend the connection's name and send to everyone. + chatMessage.text = connection.name + ": " + message; + server.sendToAllTCP(chatMessage); + return; + } + } + + public void disconnected (Connection c) { + ChatConnection connection = (ChatConnection)c; + if (connection.name != null) { + // Announce to everyone that someone (with a registered name) has left. + ChatMessage chatMessage = new ChatMessage(); + chatMessage.text = connection.name + " disconnected."; + server.sendToAllTCP(chatMessage); + updateNames(); + } + } + }); + server.bind(Network.port); + server.start(); + + // Open a window to provide an easy way to stop the server. + JFrame frame = new JFrame("Chat Server"); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.addWindowListener(new WindowAdapter() { + public void windowClosed (WindowEvent evt) { + server.stop(); + } + }); + frame.getContentPane().add(new JLabel("Close to stop the chat server.")); + frame.setSize(320, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + void updateNames () { + // Collect the names for each connection. + Connection[] connections = server.getConnections(); + ArrayList names = new ArrayList(connections.length); + for (int i = connections.length - 1; i >= 0; i--) { + ChatConnection connection = (ChatConnection)connections[i]; + names.add(connection.name); + } + // Send the names to everyone. + UpdateNames updateNames = new UpdateNames(); + updateNames.names = (String[])names.toArray(new String[names.size()]); + server.sendToAllTCP(updateNames); + } + + // This holds per connection state. + static class ChatConnection extends Connection { + public String name; + } + + public static void main (String[] args) throws IOException { + Log.set(Log.LEVEL_DEBUG); + new ChatServer(); + } +} diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/chat/Network.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/Network.java similarity index 96% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/chat/Network.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/Network.java index 1b2e2a85..4df0b823 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/chat/Network.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/Network.java @@ -1,31 +1,31 @@ - -package com.esotericsoftware.kryonet.examples.chat; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryonet.EndPoint; - -// This class is a convenient place to keep things common to both the client and server. -public class Network { - static public final int port = 54555; - - // This registers objects that are going to be sent over the network. - static public void register (EndPoint endPoint) { - Kryo kryo = endPoint.getKryo(); - kryo.register(RegisterName.class); - kryo.register(String[].class); - kryo.register(UpdateNames.class); - kryo.register(ChatMessage.class); - } - - static public class RegisterName { - public String name; - } - - static public class UpdateNames { - public String[] names; - } - - static public class ChatMessage { - public String text; - } -} + +package com.esotericsoftware.kryonet.examples.chat; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryonet.EndPoint; + +// This class is a convenient place to keep things common to both the client and server. +public class Network { + static public final int port = 54555; + + // This registers objects that are going to be sent over the network. + static public void register (EndPoint endPoint) { + Kryo kryo = endPoint.getKryo(); + kryo.register(RegisterName.class); + kryo.register(String[].class); + kryo.register(UpdateNames.class); + kryo.register(ChatMessage.class); + } + + static public class RegisterName { + public String name; + } + + static public class UpdateNames { + public String[] names; + } + + static public class ChatMessage { + public String text; + } +} diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java similarity index 96% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java index e9fa5e15..f52d2214 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java @@ -1,234 +1,234 @@ - -package com.esotericsoftware.kryonet.examples.chatrmi; - -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Container; -import java.awt.EventQueue; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.GridLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.IOException; - -import javax.swing.DefaultListModel; -import javax.swing.DefaultListSelectionModel; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JTextField; - -import com.esotericsoftware.kryonet.Client; -import com.esotericsoftware.kryonet.Connection; -import com.esotericsoftware.kryonet.Listener; -import com.esotericsoftware.kryonet.rmi.ObjectSpace; -import com.esotericsoftware.minlog.Log; - -// This class is the client for a simple chat client/server example that uses RMI. -// It is recommended to review the non-RMI chat example first. -// While this example uses only RMI, RMI can be mixed with non-RMI KryoNet usage. -// RMI has more overhead (usually 4 bytes) then just sending an object. -public class ChatRmiClient { - ChatFrame chatFrame; - Client client; - IPlayer player; - - public ChatRmiClient () { - client = new Client(); - client.start(); - - // Register the classes that will be sent over the network. - Network.register(client); - - // Get the Player on the other end of the connection. - // This allows the client to call methods on the server. - player = ObjectSpace.getRemoteObject(client, Network.PLAYER, IPlayer.class); - - client.addListener(new Listener() { - public void disconnected (Connection connection) { - EventQueue.invokeLater(new Runnable() { - public void run () { - // Closing the frame calls the close listener which will stop the client's update thread. - chatFrame.dispose(); - } - }); - } - }); - - // Request the host from the user. - String input = (String)JOptionPane.showInputDialog(null, "Host:", "Connect to chat server", JOptionPane.QUESTION_MESSAGE, - null, null, "localhost"); - if (input == null || input.trim().length() == 0) System.exit(1); - final String host = input.trim(); - - // Request the user's name. - input = (String)JOptionPane.showInputDialog(null, "Name:", "Connect to chat server", JOptionPane.QUESTION_MESSAGE, null, - null, "Test"); - if (input == null || input.trim().length() == 0) System.exit(1); - final String name = input.trim(); - - // The chat frame contains all the Swing stuff. - chatFrame = new ChatFrame(host); - // Register the chat frame so the server can call methods on it. - new ObjectSpace(client).register(Network.CHAT_FRAME, chatFrame); - // This listener is called when the send button is clicked. - chatFrame.setSendListener(new Runnable() { - public void run () { - player.sendMessage(chatFrame.getSendText()); - } - }); - // This listener is called when the chat window is closed. - chatFrame.setCloseListener(new Runnable() { - public void run () { - client.stop(); - } - }); - chatFrame.setVisible(true); - - // We'll do the connect on a new thread so the ChatFrame can show a progress bar. - // Connecting to localhost is usually so fast you won't see the progress bar. - new Thread("Connect") { - public void run () { - try { - client.connect(5000, host, Network.port); - // Server communication after connection can go here, or in Listener#connected(). - player.registerName(name); - } catch (IOException ex) { - ex.printStackTrace(); - System.exit(1); - } - } - }.start(); - } - - // This is the JFrame for the client. It implments IChatFrame so the server can call methods on it. - static private class ChatFrame extends JFrame implements IChatFrame { - CardLayout cardLayout; - JProgressBar progressBar; - JList messageList; - JTextField sendText; - JButton sendButton; - JList nameList; - - public ChatFrame (String host) { - super("Chat RMI Client"); - setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - setSize(640, 200); - setLocationRelativeTo(null); - - Container contentPane = getContentPane(); - cardLayout = new CardLayout(); - contentPane.setLayout(cardLayout); - { - JPanel panel = new JPanel(new BorderLayout()); - contentPane.add(panel, "progress"); - panel.add(new JLabel("Connecting to " + host + "...")); - { - panel.add(progressBar = new JProgressBar(), BorderLayout.SOUTH); - progressBar.setIndeterminate(true); - } - } - { - JPanel panel = new JPanel(new BorderLayout()); - contentPane.add(panel, "chat"); - { - JPanel topPanel = new JPanel(new GridLayout(1, 2)); - panel.add(topPanel); - { - topPanel.add(new JScrollPane(messageList = new JList())); - messageList.setModel(new DefaultListModel()); - } - { - topPanel.add(new JScrollPane(nameList = new JList())); - nameList.setModel(new DefaultListModel()); - } - DefaultListSelectionModel disableSelections = new DefaultListSelectionModel() { - public void setSelectionInterval (int index0, int index1) { - } - }; - messageList.setSelectionModel(disableSelections); - nameList.setSelectionModel(disableSelections); - } - { - JPanel bottomPanel = new JPanel(new GridBagLayout()); - panel.add(bottomPanel, BorderLayout.SOUTH); - bottomPanel.add(sendText = new JTextField(), new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.CENTER, - GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); - bottomPanel.add(sendButton = new JButton("Send"), new GridBagConstraints(1, 0, 1, 1, 0, 0, - GridBagConstraints.CENTER, 0, new Insets(0, 0, 0, 0), 0, 0)); - } - } - - sendText.addActionListener(new ActionListener() { - public void actionPerformed (ActionEvent e) { - sendButton.doClick(); - } - }); - } - - public void setSendListener (final Runnable listener) { - sendButton.addActionListener(new ActionListener() { - public void actionPerformed (ActionEvent evt) { - if (getSendText().length() == 0) return; - listener.run(); - sendText.setText(""); - sendText.requestFocus(); - } - }); - } - - public void setCloseListener (final Runnable listener) { - addWindowListener(new WindowAdapter() { - public void windowClosed (WindowEvent evt) { - listener.run(); - } - - public void windowActivated (WindowEvent evt) { - sendText.requestFocus(); - } - }); - } - - public String getSendText () { - return sendText.getText().trim(); - } - - // The server calls this method as needed. - public void setNames (final String[] names) { - EventQueue.invokeLater(new Runnable() { - public void run () { - cardLayout.show(getContentPane(), "chat"); - DefaultListModel model = (DefaultListModel)nameList.getModel(); - model.removeAllElements(); - for (String name : names) - model.addElement(name); - } - }); - } - - // The server calls this method as needed. - public void addMessage (final String message) { - EventQueue.invokeLater(new Runnable() { - public void run () { - DefaultListModel model = (DefaultListModel)messageList.getModel(); - model.addElement(message); - messageList.ensureIndexIsVisible(model.size() - 1); - } - }); - } - } - - public static void main (String[] args) { - Log.set(Log.LEVEL_DEBUG); - new ChatRmiClient(); - } -} + +package com.esotericsoftware.kryonet.examples.chatrmi; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Container; +import java.awt.EventQueue; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; + +import javax.swing.DefaultListModel; +import javax.swing.DefaultListSelectionModel; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JTextField; + +import com.esotericsoftware.kryonet.Client; +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.Listener; +import com.esotericsoftware.kryonet.rmi.ObjectSpace; +import com.esotericsoftware.minlog.Log; + +// This class is the client for a simple chat client/server example that uses RMI. +// It is recommended to review the non-RMI chat example first. +// While this example uses only RMI, RMI can be mixed with non-RMI KryoNet usage. +// RMI has more overhead (usually 4 bytes) then just sending an object. +public class ChatRmiClient { + ChatFrame chatFrame; + Client client; + IPlayer player; + + public ChatRmiClient () { + client = new Client(); + client.start(); + + // Register the classes that will be sent over the network. + Network.register(client); + + // Get the Player on the other end of the connection. + // This allows the client to call methods on the server. + player = ObjectSpace.getRemoteObject(client, Network.PLAYER, IPlayer.class); + + client.addListener(new Listener() { + public void disconnected (Connection connection) { + EventQueue.invokeLater(new Runnable() { + public void run () { + // Closing the frame calls the close listener which will stop the client's update thread. + chatFrame.dispose(); + } + }); + } + }); + + // Request the host from the user. + String input = (String)JOptionPane.showInputDialog(null, "Host:", "Connect to chat server", JOptionPane.QUESTION_MESSAGE, + null, null, "localhost"); + if (input == null || input.trim().length() == 0) System.exit(1); + final String host = input.trim(); + + // Request the user's name. + input = (String)JOptionPane.showInputDialog(null, "Name:", "Connect to chat server", JOptionPane.QUESTION_MESSAGE, null, + null, "Test"); + if (input == null || input.trim().length() == 0) System.exit(1); + final String name = input.trim(); + + // The chat frame contains all the Swing stuff. + chatFrame = new ChatFrame(host); + // Register the chat frame so the server can call methods on it. + new ObjectSpace(client).register(Network.CHAT_FRAME, chatFrame); + // This listener is called when the send button is clicked. + chatFrame.setSendListener(new Runnable() { + public void run () { + player.sendMessage(chatFrame.getSendText()); + } + }); + // This listener is called when the chat window is closed. + chatFrame.setCloseListener(new Runnable() { + public void run () { + client.stop(); + } + }); + chatFrame.setVisible(true); + + // We'll do the connect on a new thread so the ChatFrame can show a progress bar. + // Connecting to localhost is usually so fast you won't see the progress bar. + new Thread("Connect") { + public void run () { + try { + client.connect(5000, host, Network.port); + // Server communication after connection can go here, or in Listener#connected(). + player.registerName(name); + } catch (IOException ex) { + ex.printStackTrace(); + System.exit(1); + } + } + }.start(); + } + + // This is the JFrame for the client. It implments IChatFrame so the server can call methods on it. + static private class ChatFrame extends JFrame implements IChatFrame { + CardLayout cardLayout; + JProgressBar progressBar; + JList messageList; + JTextField sendText; + JButton sendButton; + JList nameList; + + public ChatFrame (String host) { + super("Chat RMI Client"); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setSize(640, 200); + setLocationRelativeTo(null); + + Container contentPane = getContentPane(); + cardLayout = new CardLayout(); + contentPane.setLayout(cardLayout); + { + JPanel panel = new JPanel(new BorderLayout()); + contentPane.add(panel, "progress"); + panel.add(new JLabel("Connecting to " + host + "...")); + { + panel.add(progressBar = new JProgressBar(), BorderLayout.SOUTH); + progressBar.setIndeterminate(true); + } + } + { + JPanel panel = new JPanel(new BorderLayout()); + contentPane.add(panel, "chat"); + { + JPanel topPanel = new JPanel(new GridLayout(1, 2)); + panel.add(topPanel); + { + topPanel.add(new JScrollPane(messageList = new JList())); + messageList.setModel(new DefaultListModel()); + } + { + topPanel.add(new JScrollPane(nameList = new JList())); + nameList.setModel(new DefaultListModel()); + } + DefaultListSelectionModel disableSelections = new DefaultListSelectionModel() { + public void setSelectionInterval (int index0, int index1) { + } + }; + messageList.setSelectionModel(disableSelections); + nameList.setSelectionModel(disableSelections); + } + { + JPanel bottomPanel = new JPanel(new GridBagLayout()); + panel.add(bottomPanel, BorderLayout.SOUTH); + bottomPanel.add(sendText = new JTextField(), new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.CENTER, + GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + bottomPanel.add(sendButton = new JButton("Send"), new GridBagConstraints(1, 0, 1, 1, 0, 0, + GridBagConstraints.CENTER, 0, new Insets(0, 0, 0, 0), 0, 0)); + } + } + + sendText.addActionListener(new ActionListener() { + public void actionPerformed (ActionEvent e) { + sendButton.doClick(); + } + }); + } + + public void setSendListener (final Runnable listener) { + sendButton.addActionListener(new ActionListener() { + public void actionPerformed (ActionEvent evt) { + if (getSendText().length() == 0) return; + listener.run(); + sendText.setText(""); + sendText.requestFocus(); + } + }); + } + + public void setCloseListener (final Runnable listener) { + addWindowListener(new WindowAdapter() { + public void windowClosed (WindowEvent evt) { + listener.run(); + } + + public void windowActivated (WindowEvent evt) { + sendText.requestFocus(); + } + }); + } + + public String getSendText () { + return sendText.getText().trim(); + } + + // The server calls this method as needed. + public void setNames (final String[] names) { + EventQueue.invokeLater(new Runnable() { + public void run () { + cardLayout.show(getContentPane(), "chat"); + DefaultListModel model = (DefaultListModel)nameList.getModel(); + model.removeAllElements(); + for (String name : names) + model.addElement(name); + } + }); + } + + // The server calls this method as needed. + public void addMessage (final String message) { + EventQueue.invokeLater(new Runnable() { + public void run () { + DefaultListModel model = (DefaultListModel)messageList.getModel(); + model.addElement(message); + messageList.ensureIndexIsVisible(model.size() - 1); + } + }); + } + } + + public static void main (String[] args) { + Log.set(Log.LEVEL_DEBUG); + new ChatRmiClient(); + } +} diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java similarity index 97% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java index ee37f0bd..562d4735 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java @@ -1,129 +1,129 @@ - -package com.esotericsoftware.kryonet.examples.chatrmi; - -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.IOException; -import java.util.ArrayList; - -import javax.swing.JFrame; -import javax.swing.JLabel; - -import com.esotericsoftware.kryonet.Connection; -import com.esotericsoftware.kryonet.Listener; -import com.esotericsoftware.kryonet.Server; -import com.esotericsoftware.kryonet.rmi.ObjectSpace; -import com.esotericsoftware.minlog.Log; - -// This class is the server for a simple chat client/server example that uses RMI. -// It is recommended to review the non-RMI chat example first. -// While this example uses only RMI, RMI can be mixed with non-RMI KryoNet usage. -// RMI has more overhead (usually 4 bytes) then just sending an object. -public class ChatRmiServer { - Server server; - ArrayList players = new ArrayList(); - - public ChatRmiServer () throws IOException { - server = new Server() { - protected Connection newConnection () { - // Each connection represents a player and has fields - // to store state and methods to perform actions. - Player player = new Player(); - players.add(player); - return player; - } - }; - - // Register the classes that will be sent over the network. - Network.register(server); - - server.addListener(new Listener() { - public void disconnected (Connection connection) { - Player player = (Player)connection; - players.remove(player); - if (player.name != null) { - // Announce to everyone that someone (with a registered name) has left. - String message = player.name + " disconnected."; - for (Player p : players) - p.frame.addMessage(message); - updateNames(); - } - } - }); - server.bind(Network.port); - server.start(); - - // Open a window to provide an easy way to stop the server. - JFrame frame = new JFrame("Chat RMI Server"); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - frame.addWindowListener(new WindowAdapter() { - public void windowClosed (WindowEvent evt) { - server.stop(); - } - }); - frame.getContentPane().add(new JLabel("Close to stop the chat server.")); - frame.setSize(320, 200); - frame.setLocationRelativeTo(null); - frame.setVisible(true); - } - - void updateNames () { - // Collect the names of each player. - ArrayList namesList = new ArrayList(players.size()); - for (Player player : players) - if (player.name != null) namesList.add(player.name); - // Set the names on everyone's chat frame. - String[] names = (String[])namesList.toArray(new String[namesList.size()]); - for (Player player : players) - player.frame.setNames(names); - } - - class Player extends Connection implements IPlayer { - IChatFrame frame; - String name; - - public Player () { - // Each connection has an ObjectSpace containing the Player. - // This allows the other end of the connection to call methods on the Player. - new ObjectSpace(this).register(Network.PLAYER, this); - // Get the ChatFrame on the other end of the connection. - // This allows the server to call methods on the client. - frame = ObjectSpace.getRemoteObject(this, Network.CHAT_FRAME, IChatFrame.class); - } - - public void registerName (String name) { - // Do nothing if the player already registered a name. - if (this.name != null) return; - // Do nothing if the name is invalid. - if (name == null) return; - name = name.trim(); - if (name.length() == 0) return; - // Store the player's name. - this.name = name; - // Add a "connected" message to everyone's chat frame, except the new player. - String message = name + " connected."; - for (Player player : players) - if (player != this) player.frame.addMessage(message); - // Set the names on everyone's chat frame. - updateNames(); - } - - public void sendMessage (String message) { - // Do nothing if a player tries to chat before registering a name. - if (this.name == null) return; - // Do nothing if the chat message is invalid. - if (message == null) return; - message = message.trim(); - if (message.length() == 0) return; - // Prepend the player's name and add to everyone's chat frame. - message = this.name + ": " + message; - for (Player player : players) - player.frame.addMessage(message); - } - } - - public static void main (String[] args) throws IOException { - Log.set(Log.LEVEL_DEBUG); - new ChatRmiServer(); - } -} + +package com.esotericsoftware.kryonet.examples.chatrmi; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.util.ArrayList; + +import javax.swing.JFrame; +import javax.swing.JLabel; + +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.Listener; +import com.esotericsoftware.kryonet.Server; +import com.esotericsoftware.kryonet.rmi.ObjectSpace; +import com.esotericsoftware.minlog.Log; + +// This class is the server for a simple chat client/server example that uses RMI. +// It is recommended to review the non-RMI chat example first. +// While this example uses only RMI, RMI can be mixed with non-RMI KryoNet usage. +// RMI has more overhead (usually 4 bytes) then just sending an object. +public class ChatRmiServer { + Server server; + ArrayList players = new ArrayList(); + + public ChatRmiServer () throws IOException { + server = new Server() { + protected Connection newConnection () { + // Each connection represents a player and has fields + // to store state and methods to perform actions. + Player player = new Player(); + players.add(player); + return player; + } + }; + + // Register the classes that will be sent over the network. + Network.register(server); + + server.addListener(new Listener() { + public void disconnected (Connection connection) { + Player player = (Player)connection; + players.remove(player); + if (player.name != null) { + // Announce to everyone that someone (with a registered name) has left. + String message = player.name + " disconnected."; + for (Player p : players) + p.frame.addMessage(message); + updateNames(); + } + } + }); + server.bind(Network.port); + server.start(); + + // Open a window to provide an easy way to stop the server. + JFrame frame = new JFrame("Chat RMI Server"); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.addWindowListener(new WindowAdapter() { + public void windowClosed (WindowEvent evt) { + server.stop(); + } + }); + frame.getContentPane().add(new JLabel("Close to stop the chat server.")); + frame.setSize(320, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + void updateNames () { + // Collect the names of each player. + ArrayList namesList = new ArrayList(players.size()); + for (Player player : players) + if (player.name != null) namesList.add(player.name); + // Set the names on everyone's chat frame. + String[] names = (String[])namesList.toArray(new String[namesList.size()]); + for (Player player : players) + player.frame.setNames(names); + } + + class Player extends Connection implements IPlayer { + IChatFrame frame; + String name; + + public Player () { + // Each connection has an ObjectSpace containing the Player. + // This allows the other end of the connection to call methods on the Player. + new ObjectSpace(this).register(Network.PLAYER, this); + // Get the ChatFrame on the other end of the connection. + // This allows the server to call methods on the client. + frame = ObjectSpace.getRemoteObject(this, Network.CHAT_FRAME, IChatFrame.class); + } + + public void registerName (String name) { + // Do nothing if the player already registered a name. + if (this.name != null) return; + // Do nothing if the name is invalid. + if (name == null) return; + name = name.trim(); + if (name.length() == 0) return; + // Store the player's name. + this.name = name; + // Add a "connected" message to everyone's chat frame, except the new player. + String message = name + " connected."; + for (Player player : players) + if (player != this) player.frame.addMessage(message); + // Set the names on everyone's chat frame. + updateNames(); + } + + public void sendMessage (String message) { + // Do nothing if a player tries to chat before registering a name. + if (this.name == null) return; + // Do nothing if the chat message is invalid. + if (message == null) return; + message = message.trim(); + if (message.length() == 0) return; + // Prepend the player's name and add to everyone's chat frame. + message = this.name + ": " + message; + for (Player player : players) + player.frame.addMessage(message); + } + } + + public static void main (String[] args) throws IOException { + Log.set(Log.LEVEL_DEBUG); + new ChatRmiServer(); + } +} diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java similarity index 96% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java index 5e747f61..8c2db460 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java @@ -1,9 +1,9 @@ - -package com.esotericsoftware.kryonet.examples.chatrmi; - -// This class represents the chat window on the client. -public interface IChatFrame { - public void addMessage (String message); - - public void setNames (String[] names); -} + +package com.esotericsoftware.kryonet.examples.chatrmi; + +// This class represents the chat window on the client. +public interface IChatFrame { + public void addMessage (String message); + + public void setNames (String[] names); +} diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java similarity index 96% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java index 1129e091..896d7176 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java @@ -1,9 +1,9 @@ - -package com.esotericsoftware.kryonet.examples.chatrmi; - -// This class represents a player on the server. -public interface IPlayer { - public void registerName (String name); - - public void sendMessage (String message); -} + +package com.esotericsoftware.kryonet.examples.chatrmi; + +// This class represents a player on the server. +public interface IPlayer { + public void registerName (String name); + + public void sendMessage (String message); +} diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/Network.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/Network.java similarity index 97% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/Network.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/Network.java index 0fb47ca3..8cd155d8 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/chatrmi/Network.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/Network.java @@ -1,27 +1,27 @@ - -package com.esotericsoftware.kryonet.examples.chatrmi; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryonet.EndPoint; -import com.esotericsoftware.kryonet.rmi.ObjectSpace; - -public class Network { - static public final int port = 54777; - - // These IDs are used to register objects in ObjectSpaces. - static public final short PLAYER = 1; - static public final short CHAT_FRAME = 2; - - // This registers objects that are going to be sent over the network. - static public void register (EndPoint endPoint) { - Kryo kryo = endPoint.getKryo(); - // This must be called in order to use ObjectSpaces. - ObjectSpace.registerClasses(kryo); - // The interfaces that will be used as remote objects must be registered. - kryo.register(IPlayer.class); - kryo.register(IChatFrame.class); - // The classes of all method parameters and return values - // for remote objects must also be registered. - kryo.register(String[].class); - } -} + +package com.esotericsoftware.kryonet.examples.chatrmi; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryonet.EndPoint; +import com.esotericsoftware.kryonet.rmi.ObjectSpace; + +public class Network { + static public final int port = 54777; + + // These IDs are used to register objects in ObjectSpaces. + static public final short PLAYER = 1; + static public final short CHAT_FRAME = 2; + + // This registers objects that are going to be sent over the network. + static public void register (EndPoint endPoint) { + Kryo kryo = endPoint.getKryo(); + // This must be called in order to use ObjectSpaces. + ObjectSpace.registerClasses(kryo); + // The interfaces that will be used as remote objects must be registered. + kryo.register(IPlayer.class); + kryo.register(IChatFrame.class); + // The classes of all method parameters and return values + // for remote objects must also be registered. + kryo.register(String[].class); + } +} diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/position/Character.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/Character.java similarity index 95% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/position/Character.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/Character.java index 037f8c7e..25de02b9 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/position/Character.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/Character.java @@ -1,8 +1,8 @@ - -package com.esotericsoftware.kryonet.examples.position; - -public class Character { - public String name; - public String otherStuff; - public int id, x, y; -} + +package com.esotericsoftware.kryonet.examples.position; + +public class Character { + public String name; + public String otherStuff; + public int id, x, y; +} diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/position/Network.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/Network.java similarity index 95% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/position/Network.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/Network.java index b82598f9..3398596d 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/position/Network.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/Network.java @@ -1,51 +1,51 @@ - -package com.esotericsoftware.kryonet.examples.position; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryonet.EndPoint; - -// This class is a convenient place to keep things common to both the client and server. -public class Network { - static public final int port = 54555; - - // This registers objects that are going to be sent over the network. - static public void register (EndPoint endPoint) { - Kryo kryo = endPoint.getKryo(); - kryo.register(Login.class); - kryo.register(RegistrationRequired.class); - kryo.register(Register.class); - kryo.register(AddCharacter.class); - kryo.register(UpdateCharacter.class); - kryo.register(RemoveCharacter.class); - kryo.register(Character.class); - kryo.register(MoveCharacter.class); - } - - static public class Login { - public String name; - } - - static public class RegistrationRequired { - } - - static public class Register { - public String name; - public String otherStuff; - } - - static public class UpdateCharacter { - public int id, x, y; - } - - static public class AddCharacter { - public Character character; - } - - static public class RemoveCharacter { - public int id; - } - - static public class MoveCharacter { - public int x, y; - } -} + +package com.esotericsoftware.kryonet.examples.position; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryonet.EndPoint; + +// This class is a convenient place to keep things common to both the client and server. +public class Network { + static public final int port = 54555; + + // This registers objects that are going to be sent over the network. + static public void register (EndPoint endPoint) { + Kryo kryo = endPoint.getKryo(); + kryo.register(Login.class); + kryo.register(RegistrationRequired.class); + kryo.register(Register.class); + kryo.register(AddCharacter.class); + kryo.register(UpdateCharacter.class); + kryo.register(RemoveCharacter.class); + kryo.register(Character.class); + kryo.register(MoveCharacter.class); + } + + static public class Login { + public String name; + } + + static public class RegistrationRequired { + } + + static public class Register { + public String name; + public String otherStuff; + } + + static public class UpdateCharacter { + public int id, x, y; + } + + static public class AddCharacter { + public Character character; + } + + static public class RemoveCharacter { + public int id; + } + + static public class MoveCharacter { + public int x, y; + } +} diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/position/PositionClient.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionClient.java similarity index 96% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/position/PositionClient.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionClient.java index 3947e30e..7dae6833 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/position/PositionClient.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionClient.java @@ -1,163 +1,163 @@ - -package com.esotericsoftware.kryonet.examples.position; - -import java.io.IOException; -import java.util.HashMap; - -import javax.swing.JOptionPane; - -import com.esotericsoftware.kryonet.Client; -import com.esotericsoftware.kryonet.Connection; -import com.esotericsoftware.kryonet.Listener; -import com.esotericsoftware.kryonet.Listener.ThreadedListener; -import com.esotericsoftware.kryonet.examples.position.Network.AddCharacter; -import com.esotericsoftware.kryonet.examples.position.Network.Login; -import com.esotericsoftware.kryonet.examples.position.Network.MoveCharacter; -import com.esotericsoftware.kryonet.examples.position.Network.Register; -import com.esotericsoftware.kryonet.examples.position.Network.RegistrationRequired; -import com.esotericsoftware.kryonet.examples.position.Network.RemoveCharacter; -import com.esotericsoftware.kryonet.examples.position.Network.UpdateCharacter; -import com.esotericsoftware.minlog.Log; - -public class PositionClient { - UI ui; - Client client; - String name; - - public PositionClient () { - client = new Client(); - client.start(); - - // For consistency, the classes to be sent over the network are - // registered by the same method for both the client and server. - Network.register(client); - - // ThreadedListener runs the listener methods on a different thread. - client.addListener(new ThreadedListener(new Listener() { - public void connected (Connection connection) { - } - - public void received (Connection connection, Object object) { - if (object instanceof RegistrationRequired) { - Register register = new Register(); - register.name = name; - register.otherStuff = ui.inputOtherStuff(); - client.sendTCP(register); - } - - if (object instanceof AddCharacter) { - AddCharacter msg = (AddCharacter)object; - ui.addCharacter(msg.character); - return; - } - - if (object instanceof UpdateCharacter) { - ui.updateCharacter((UpdateCharacter)object); - return; - } - - if (object instanceof RemoveCharacter) { - RemoveCharacter msg = (RemoveCharacter)object; - ui.removeCharacter(msg.id); - return; - } - } - - public void disconnected (Connection connection) { - System.exit(0); - } - })); - - ui = new UI(); - - String host = ui.inputHost(); - try { - client.connect(5000, host, Network.port); - // Server communication after connection can go here, or in Listener#connected(). - } catch (IOException ex) { - ex.printStackTrace(); - } - - name = ui.inputName(); - Login login = new Login(); - login.name = name; - client.sendTCP(login); - - while (true) { - int ch; - try { - ch = System.in.read(); - } catch (IOException ex) { - ex.printStackTrace(); - break; - } - - MoveCharacter msg = new MoveCharacter(); - switch (ch) { - case 'w': - msg.y = -1; - break; - case 's': - msg.y = 1; - break; - case 'a': - msg.x = -1; - break; - case 'd': - msg.x = 1; - break; - default: - msg = null; - } - if (msg != null) client.sendTCP(msg); - } - } - - static class UI { - HashMap characters = new HashMap(); - - public String inputHost () { - String input = (String)JOptionPane.showInputDialog(null, "Host:", "Connect to server", JOptionPane.QUESTION_MESSAGE, - null, null, "localhost"); - if (input == null || input.trim().length() == 0) System.exit(1); - return input.trim(); - } - - public String inputName () { - String input = (String)JOptionPane.showInputDialog(null, "Name:", "Connect to server", JOptionPane.QUESTION_MESSAGE, - null, null, "Test"); - if (input == null || input.trim().length() == 0) System.exit(1); - return input.trim(); - } - - public String inputOtherStuff () { - String input = (String)JOptionPane.showInputDialog(null, "Other Stuff:", "Create account", JOptionPane.QUESTION_MESSAGE, - null, null, "other stuff"); - if (input == null || input.trim().length() == 0) System.exit(1); - return input.trim(); - } - - public void addCharacter (Character character) { - characters.put(character.id, character); - System.out.println(character.name + " added at " + character.x + ", " + character.y); - } - - public void updateCharacter (UpdateCharacter msg) { - Character character = characters.get(msg.id); - if (character == null) return; - character.x = msg.x; - character.y = msg.y; - System.out.println(character.name + " moved to " + character.x + ", " + character.y); - } - - public void removeCharacter (int id) { - Character character = characters.remove(id); - if (character != null) System.out.println(character.name + " removed"); - } - } - - public static void main (String[] args) { - Log.set(Log.LEVEL_DEBUG); - new PositionClient(); - } -} + +package com.esotericsoftware.kryonet.examples.position; + +import java.io.IOException; +import java.util.HashMap; + +import javax.swing.JOptionPane; + +import com.esotericsoftware.kryonet.Client; +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.Listener; +import com.esotericsoftware.kryonet.Listener.ThreadedListener; +import com.esotericsoftware.kryonet.examples.position.Network.AddCharacter; +import com.esotericsoftware.kryonet.examples.position.Network.Login; +import com.esotericsoftware.kryonet.examples.position.Network.MoveCharacter; +import com.esotericsoftware.kryonet.examples.position.Network.Register; +import com.esotericsoftware.kryonet.examples.position.Network.RegistrationRequired; +import com.esotericsoftware.kryonet.examples.position.Network.RemoveCharacter; +import com.esotericsoftware.kryonet.examples.position.Network.UpdateCharacter; +import com.esotericsoftware.minlog.Log; + +public class PositionClient { + UI ui; + Client client; + String name; + + public PositionClient () { + client = new Client(); + client.start(); + + // For consistency, the classes to be sent over the network are + // registered by the same method for both the client and server. + Network.register(client); + + // ThreadedListener runs the listener methods on a different thread. + client.addListener(new ThreadedListener(new Listener() { + public void connected (Connection connection) { + } + + public void received (Connection connection, Object object) { + if (object instanceof RegistrationRequired) { + Register register = new Register(); + register.name = name; + register.otherStuff = ui.inputOtherStuff(); + client.sendTCP(register); + } + + if (object instanceof AddCharacter) { + AddCharacter msg = (AddCharacter)object; + ui.addCharacter(msg.character); + return; + } + + if (object instanceof UpdateCharacter) { + ui.updateCharacter((UpdateCharacter)object); + return; + } + + if (object instanceof RemoveCharacter) { + RemoveCharacter msg = (RemoveCharacter)object; + ui.removeCharacter(msg.id); + return; + } + } + + public void disconnected (Connection connection) { + System.exit(0); + } + })); + + ui = new UI(); + + String host = ui.inputHost(); + try { + client.connect(5000, host, Network.port); + // Server communication after connection can go here, or in Listener#connected(). + } catch (IOException ex) { + ex.printStackTrace(); + } + + name = ui.inputName(); + Login login = new Login(); + login.name = name; + client.sendTCP(login); + + while (true) { + int ch; + try { + ch = System.in.read(); + } catch (IOException ex) { + ex.printStackTrace(); + break; + } + + MoveCharacter msg = new MoveCharacter(); + switch (ch) { + case 'w': + msg.y = -1; + break; + case 's': + msg.y = 1; + break; + case 'a': + msg.x = -1; + break; + case 'd': + msg.x = 1; + break; + default: + msg = null; + } + if (msg != null) client.sendTCP(msg); + } + } + + static class UI { + HashMap characters = new HashMap(); + + public String inputHost () { + String input = (String)JOptionPane.showInputDialog(null, "Host:", "Connect to server", JOptionPane.QUESTION_MESSAGE, + null, null, "localhost"); + if (input == null || input.trim().length() == 0) System.exit(1); + return input.trim(); + } + + public String inputName () { + String input = (String)JOptionPane.showInputDialog(null, "Name:", "Connect to server", JOptionPane.QUESTION_MESSAGE, + null, null, "Test"); + if (input == null || input.trim().length() == 0) System.exit(1); + return input.trim(); + } + + public String inputOtherStuff () { + String input = (String)JOptionPane.showInputDialog(null, "Other Stuff:", "Create account", JOptionPane.QUESTION_MESSAGE, + null, null, "other stuff"); + if (input == null || input.trim().length() == 0) System.exit(1); + return input.trim(); + } + + public void addCharacter (Character character) { + characters.put(character.id, character); + System.out.println(character.name + " added at " + character.x + ", " + character.y); + } + + public void updateCharacter (UpdateCharacter msg) { + Character character = characters.get(msg.id); + if (character == null) return; + character.x = msg.x; + character.y = msg.y; + System.out.println(character.name + " moved to " + character.x + ", " + character.y); + } + + public void removeCharacter (int id) { + Character character = characters.remove(id); + if (character != null) System.out.println(character.name + " removed"); + } + } + + public static void main (String[] args) { + Log.set(Log.LEVEL_DEBUG); + new PositionClient(); + } +} diff --git a/kryonet/examples/com/esotericsoftware/kryonet/examples/position/PositionServer.java b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionServer.java similarity index 96% rename from kryonet/examples/com/esotericsoftware/kryonet/examples/position/PositionServer.java rename to kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionServer.java index 358484ef..2984004d 100644 --- a/kryonet/examples/com/esotericsoftware/kryonet/examples/position/PositionServer.java +++ b/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionServer.java @@ -1,242 +1,242 @@ - -package com.esotericsoftware.kryonet.examples.position; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.HashSet; - -import com.esotericsoftware.kryonet.Connection; -import com.esotericsoftware.kryonet.Listener; -import com.esotericsoftware.kryonet.Server; -import com.esotericsoftware.kryonet.examples.position.Network.AddCharacter; -import com.esotericsoftware.kryonet.examples.position.Network.Login; -import com.esotericsoftware.kryonet.examples.position.Network.MoveCharacter; -import com.esotericsoftware.kryonet.examples.position.Network.Register; -import com.esotericsoftware.kryonet.examples.position.Network.RegistrationRequired; -import com.esotericsoftware.kryonet.examples.position.Network.RemoveCharacter; -import com.esotericsoftware.kryonet.examples.position.Network.UpdateCharacter; -import com.esotericsoftware.minlog.Log; - -public class PositionServer { - Server server; - HashSet loggedIn = new HashSet(); - - public PositionServer () throws IOException { - server = new Server() { - protected Connection newConnection () { - // By providing our own connection implementation, we can store per - // connection state without a connection ID to state look up. - return new CharacterConnection(); - } - }; - - // For consistency, the classes to be sent over the network are - // registered by the same method for both the client and server. - Network.register(server); - - server.addListener(new Listener() { - public void received (Connection c, Object object) { - // We know all connections for this server are actually CharacterConnections. - CharacterConnection connection = (CharacterConnection)c; - Character character = connection.character; - - if (object instanceof Login) { - // Ignore if already logged in. - if (character != null) return; - - // Reject if the name is invalid. - String name = ((Login)object).name; - if (!isValid(name)) { - c.close(); - return; - } - - // Reject if already logged in. - for (Character other : loggedIn) { - if (other.name.equals(name)) { - c.close(); - return; - } - } - - character = loadCharacter(name); - - // Reject if couldn't load character. - if (character == null) { - c.sendTCP(new RegistrationRequired()); - return; - } - - loggedIn(connection, character); - return; - } - - if (object instanceof Register) { - // Ignore if already logged in. - if (character != null) return; - - Register register = (Register)object; - - // Reject if the login is invalid. - if (!isValid(register.name)) { - c.close(); - return; - } - if (!isValid(register.otherStuff)) { - c.close(); - return; - } - - // Reject if character alread exists. - if (loadCharacter(register.name) != null) { - c.close(); - return; - } - - character = new Character(); - character.name = register.name; - character.otherStuff = register.otherStuff; - character.x = 0; - character.y = 0; - if (!saveCharacter(character)) { - c.close(); - return; - } - - loggedIn(connection, character); - return; - } - - if (object instanceof MoveCharacter) { - // Ignore if not logged in. - if (character == null) return; - - MoveCharacter msg = (MoveCharacter)object; - - // Ignore if invalid move. - if (Math.abs(msg.x) != 1 && Math.abs(msg.y) != 1) return; - - character.x += msg.x; - character.y += msg.y; - if (!saveCharacter(character)) { - connection.close(); - return; - } - - UpdateCharacter update = new UpdateCharacter(); - update.id = character.id; - update.x = character.x; - update.y = character.y; - server.sendToAllTCP(update); - return; - } - } - - private boolean isValid (String value) { - if (value == null) return false; - value = value.trim(); - if (value.length() == 0) return false; - return true; - } - - public void disconnected (Connection c) { - CharacterConnection connection = (CharacterConnection)c; - if (connection.character != null) { - loggedIn.remove(connection.character); - - RemoveCharacter removeCharacter = new RemoveCharacter(); - removeCharacter.id = connection.character.id; - server.sendToAllTCP(removeCharacter); - } - } - }); - server.bind(Network.port); - server.start(); - } - - void loggedIn (CharacterConnection c, Character character) { - c.character = character; - - // Add existing characters to new logged in connection. - for (Character other : loggedIn) { - AddCharacter addCharacter = new AddCharacter(); - addCharacter.character = other; - c.sendTCP(addCharacter); - } - - loggedIn.add(character); - - // Add logged in character to all connections. - AddCharacter addCharacter = new AddCharacter(); - addCharacter.character = character; - server.sendToAllTCP(addCharacter); - } - - boolean saveCharacter (Character character) { - File file = new File("characters", character.name.toLowerCase()); - file.getParentFile().mkdirs(); - - if (character.id == 0) { - String[] children = file.getParentFile().list(); - if (children == null) return false; - character.id = children.length + 1; - } - - DataOutputStream output = null; - try { - output = new DataOutputStream(new FileOutputStream(file)); - output.writeInt(character.id); - output.writeUTF(character.otherStuff); - output.writeInt(character.x); - output.writeInt(character.y); - return true; - } catch (IOException ex) { - ex.printStackTrace(); - return false; - } finally { - try { - output.close(); - } catch (IOException ignored) { - } - } - } - - Character loadCharacter (String name) { - File file = new File("characters", name.toLowerCase()); - if (!file.exists()) return null; - DataInputStream input = null; - try { - input = new DataInputStream(new FileInputStream(file)); - Character character = new Character(); - character.id = input.readInt(); - character.name = name; - character.otherStuff = input.readUTF(); - character.x = input.readInt(); - character.y = input.readInt(); - input.close(); - return character; - } catch (IOException ex) { - ex.printStackTrace(); - return null; - } finally { - try { - if (input != null) input.close(); - } catch (IOException ignored) { - } - } - } - - // This holds per connection state. - static class CharacterConnection extends Connection { - public Character character; - } - - public static void main (String[] args) throws IOException { - Log.set(Log.LEVEL_DEBUG); - new PositionServer(); - } -} + +package com.esotericsoftware.kryonet.examples.position; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashSet; + +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.Listener; +import com.esotericsoftware.kryonet.Server; +import com.esotericsoftware.kryonet.examples.position.Network.AddCharacter; +import com.esotericsoftware.kryonet.examples.position.Network.Login; +import com.esotericsoftware.kryonet.examples.position.Network.MoveCharacter; +import com.esotericsoftware.kryonet.examples.position.Network.Register; +import com.esotericsoftware.kryonet.examples.position.Network.RegistrationRequired; +import com.esotericsoftware.kryonet.examples.position.Network.RemoveCharacter; +import com.esotericsoftware.kryonet.examples.position.Network.UpdateCharacter; +import com.esotericsoftware.minlog.Log; + +public class PositionServer { + Server server; + HashSet loggedIn = new HashSet(); + + public PositionServer () throws IOException { + server = new Server() { + protected Connection newConnection () { + // By providing our own connection implementation, we can store per + // connection state without a connection ID to state look up. + return new CharacterConnection(); + } + }; + + // For consistency, the classes to be sent over the network are + // registered by the same method for both the client and server. + Network.register(server); + + server.addListener(new Listener() { + public void received (Connection c, Object object) { + // We know all connections for this server are actually CharacterConnections. + CharacterConnection connection = (CharacterConnection)c; + Character character = connection.character; + + if (object instanceof Login) { + // Ignore if already logged in. + if (character != null) return; + + // Reject if the name is invalid. + String name = ((Login)object).name; + if (!isValid(name)) { + c.close(); + return; + } + + // Reject if already logged in. + for (Character other : loggedIn) { + if (other.name.equals(name)) { + c.close(); + return; + } + } + + character = loadCharacter(name); + + // Reject if couldn't load character. + if (character == null) { + c.sendTCP(new RegistrationRequired()); + return; + } + + loggedIn(connection, character); + return; + } + + if (object instanceof Register) { + // Ignore if already logged in. + if (character != null) return; + + Register register = (Register)object; + + // Reject if the login is invalid. + if (!isValid(register.name)) { + c.close(); + return; + } + if (!isValid(register.otherStuff)) { + c.close(); + return; + } + + // Reject if character alread exists. + if (loadCharacter(register.name) != null) { + c.close(); + return; + } + + character = new Character(); + character.name = register.name; + character.otherStuff = register.otherStuff; + character.x = 0; + character.y = 0; + if (!saveCharacter(character)) { + c.close(); + return; + } + + loggedIn(connection, character); + return; + } + + if (object instanceof MoveCharacter) { + // Ignore if not logged in. + if (character == null) return; + + MoveCharacter msg = (MoveCharacter)object; + + // Ignore if invalid move. + if (Math.abs(msg.x) != 1 && Math.abs(msg.y) != 1) return; + + character.x += msg.x; + character.y += msg.y; + if (!saveCharacter(character)) { + connection.close(); + return; + } + + UpdateCharacter update = new UpdateCharacter(); + update.id = character.id; + update.x = character.x; + update.y = character.y; + server.sendToAllTCP(update); + return; + } + } + + private boolean isValid (String value) { + if (value == null) return false; + value = value.trim(); + if (value.length() == 0) return false; + return true; + } + + public void disconnected (Connection c) { + CharacterConnection connection = (CharacterConnection)c; + if (connection.character != null) { + loggedIn.remove(connection.character); + + RemoveCharacter removeCharacter = new RemoveCharacter(); + removeCharacter.id = connection.character.id; + server.sendToAllTCP(removeCharacter); + } + } + }); + server.bind(Network.port); + server.start(); + } + + void loggedIn (CharacterConnection c, Character character) { + c.character = character; + + // Add existing characters to new logged in connection. + for (Character other : loggedIn) { + AddCharacter addCharacter = new AddCharacter(); + addCharacter.character = other; + c.sendTCP(addCharacter); + } + + loggedIn.add(character); + + // Add logged in character to all connections. + AddCharacter addCharacter = new AddCharacter(); + addCharacter.character = character; + server.sendToAllTCP(addCharacter); + } + + boolean saveCharacter (Character character) { + File file = new File("characters", character.name.toLowerCase()); + file.getParentFile().mkdirs(); + + if (character.id == 0) { + String[] children = file.getParentFile().list(); + if (children == null) return false; + character.id = children.length + 1; + } + + DataOutputStream output = null; + try { + output = new DataOutputStream(new FileOutputStream(file)); + output.writeInt(character.id); + output.writeUTF(character.otherStuff); + output.writeInt(character.x); + output.writeInt(character.y); + return true; + } catch (IOException ex) { + ex.printStackTrace(); + return false; + } finally { + try { + output.close(); + } catch (IOException ignored) { + } + } + } + + Character loadCharacter (String name) { + File file = new File("characters", name.toLowerCase()); + if (!file.exists()) return null; + DataInputStream input = null; + try { + input = new DataInputStream(new FileInputStream(file)); + Character character = new Character(); + character.id = input.readInt(); + character.name = name; + character.otherStuff = input.readUTF(); + character.x = input.readInt(); + character.y = input.readInt(); + input.close(); + return character; + } catch (IOException ex) { + ex.printStackTrace(); + return null; + } finally { + try { + if (input != null) input.close(); + } catch (IOException ignored) { + } + } + } + + // This holds per connection state. + static class CharacterConnection extends Connection { + public Character character; + } + + public static void main (String[] args) throws IOException { + Log.set(Log.LEVEL_DEBUG); + new PositionServer(); + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/Client.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/Client.java similarity index 97% rename from kryonet/src/com/esotericsoftware/kryonet/Client.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/Client.java index 5dc1727c..1a85434e 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/Client.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/Client.java @@ -1,492 +1,492 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.NetworkInterface; -import java.net.SocketTimeoutException; -import java.nio.ByteBuffer; -import java.nio.channels.CancelledKeyException; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.security.AccessControlException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryonet.FrameworkMessage.DiscoverHost; -import com.esotericsoftware.kryonet.FrameworkMessage.RegisterTCP; -import com.esotericsoftware.kryonet.FrameworkMessage.RegisterUDP; - -import static com.esotericsoftware.minlog.Log.*; - -/** Represents a TCP and optionally a UDP connection to a {@link Server}. - * @author Nathan Sweet */ -public class Client extends Connection implements EndPoint { - static { - try { - // Needed for NIO selectors on Android 2.2. - System.setProperty("java.net.preferIPv6Addresses", "false"); - } catch (AccessControlException ignored) { - } - } - - private final Serialization serialization; - private Selector selector; - private int emptySelects; - private volatile boolean tcpRegistered, udpRegistered; - private Object tcpRegistrationLock = new Object(); - private Object udpRegistrationLock = new Object(); - private volatile boolean shutdown; - private final Object updateLock = new Object(); - private Thread updateThread; - private int connectTimeout; - private InetAddress connectHost; - private int connectTcpPort; - private int connectUdpPort; - private boolean isClosed; - - /** Creates a Client with a write buffer size of 8192 and an object buffer size of 2048. */ - public Client () { - this(8192, 2048); - } - - /** @param writeBufferSize One buffer of this size is allocated. Objects are serialized to the write buffer where the bytes are - * queued until they can be written to the TCP socket. - *

    - * Normally the socket is writable and the bytes are written immediately. If the socket cannot be written to and - * enough serialized objects are queued to overflow the buffer, then the connection will be closed. - *

    - * The write buffer should be sized at least as large as the largest object that will be sent, plus some head room to - * allow for some serialized objects to be queued in case the buffer is temporarily not writable. The amount of head - * room needed is dependent upon the size of objects being sent and how often they are sent. - * @param objectBufferSize One (using only TCP) or three (using both TCP and UDP) buffers of this size are allocated. These - * buffers are used to hold the bytes for a single object graph until it can be sent over the network or - * deserialized. - *

    - * The object buffers should be sized at least as large as the largest object that will be sent or received. */ - public Client (int writeBufferSize, int objectBufferSize) { - this(writeBufferSize, objectBufferSize, new KryoSerialization()); - } - - public Client (int writeBufferSize, int objectBufferSize, Serialization serialization) { - super(); - endPoint = this; - - this.serialization = serialization; - - initialize(serialization, writeBufferSize, objectBufferSize); - - try { - selector = Selector.open(); - } catch (IOException ex) { - throw new RuntimeException("Error opening selector.", ex); - } - } - - public Serialization getSerialization () { - return serialization; - } - - public Kryo getKryo () { - return ((KryoSerialization)serialization).getKryo(); - } - - /** Opens a TCP only client. - * @see #connect(int, InetAddress, int, int) */ - public void connect (int timeout, String host, int tcpPort) throws IOException { - connect(timeout, InetAddress.getByName(host), tcpPort, -1); - } - - /** Opens a TCP and UDP client. - * @see #connect(int, InetAddress, int, int) */ - public void connect (int timeout, String host, int tcpPort, int udpPort) throws IOException { - connect(timeout, InetAddress.getByName(host), tcpPort, udpPort); - } - - /** Opens a TCP only client. - * @see #connect(int, InetAddress, int, int) */ - public void connect (int timeout, InetAddress host, int tcpPort) throws IOException { - connect(timeout, host, tcpPort, -1); - } - - /** Opens a TCP and UDP client. Blocks until the connection is complete or the timeout is reached. - *

    - * Because the framework must perform some minimal communication before the connection is considered successful, - * {@link #update(int)} must be called on a separate thread during the connection process. - * @throws IllegalStateException if called from the connection's update thread. - * @throws IOException if the client could not be opened or connecting times out. */ - public void connect (int timeout, InetAddress host, int tcpPort, int udpPort) throws IOException { - if (host == null) throw new IllegalArgumentException("host cannot be null."); - if (Thread.currentThread() == getUpdateThread()) - throw new IllegalStateException("Cannot connect on the connection's update thread."); - this.connectTimeout = timeout; - this.connectHost = host; - this.connectTcpPort = tcpPort; - this.connectUdpPort = udpPort; - close(); - if (INFO) { - if (udpPort != -1) - info("Connecting: " + host + ":" + tcpPort + "/" + udpPort); - else - info("Connecting: " + host + ":" + tcpPort); - } - id = -1; - try { - if (udpPort != -1) udp = new UdpConnection(serialization, tcp.readBuffer.capacity()); - - long endTime; - synchronized (updateLock) { - tcpRegistered = false; - selector.wakeup(); - endTime = System.currentTimeMillis() + timeout; - tcp.connect(selector, new InetSocketAddress(host, tcpPort), 5000); - } - - // Wait for RegisterTCP. - synchronized (tcpRegistrationLock) { - while (!tcpRegistered && System.currentTimeMillis() < endTime) { - try { - tcpRegistrationLock.wait(100); - } catch (InterruptedException ignored) { - } - } - if (!tcpRegistered) { - throw new SocketTimeoutException("Connected, but timed out during TCP registration.\n" - + "Note: Client#update must be called in a separate thread during connect."); - } - } - - if (udpPort != -1) { - InetSocketAddress udpAddress = new InetSocketAddress(host, udpPort); - synchronized (updateLock) { - udpRegistered = false; - selector.wakeup(); - udp.connect(selector, udpAddress); - } - - // Wait for RegisterUDP reply. - synchronized (udpRegistrationLock) { - while (!udpRegistered && System.currentTimeMillis() < endTime) { - RegisterUDP registerUDP = new RegisterUDP(); - registerUDP.connectionID = id; - udp.send(this, registerUDP, udpAddress); - try { - udpRegistrationLock.wait(100); - } catch (InterruptedException ignored) { - } - } - if (!udpRegistered) - throw new SocketTimeoutException("Connected, but timed out during UDP registration: " + host + ":" + udpPort); - } - } - } catch (IOException ex) { - close(); - throw ex; - } - } - - /** Calls {@link #connect(int, InetAddress, int) connect} with the values last passed to connect. - * @throws IllegalStateException if connect has never been called. */ - public void reconnect () throws IOException { - reconnect(connectTimeout); - } - - /** Calls {@link #connect(int, InetAddress, int) connect} with the specified timeout and the other values last passed to - * connect. - * @throws IllegalStateException if connect has never been called. */ - public void reconnect (int timeout) throws IOException { - if (connectHost == null) throw new IllegalStateException("This client has never been connected."); - connect(connectTimeout, connectHost, connectTcpPort, connectUdpPort); - } - - /** Reads or writes any pending data for this client. Multiple threads should not call this method at the same time. - * @param timeout Wait for up to the specified milliseconds for data to be ready to process. May be zero to return immediately - * if there is no data to process. */ - public void update (int timeout) throws IOException { - updateThread = Thread.currentThread(); - synchronized (updateLock) { // Blocks to avoid a select while the selector is used to bind the server connection. - } - long startTime = System.currentTimeMillis(); - int select = 0; - if (timeout > 0) { - select = selector.select(timeout); - } else { - select = selector.selectNow(); - } - if (select == 0) { - emptySelects++; - if (emptySelects == 100) { - emptySelects = 0; - // NIO freaks and returns immediately with 0 sometimes, so try to keep from hogging the CPU. - long elapsedTime = System.currentTimeMillis() - startTime; - try { - if (elapsedTime < 25) Thread.sleep(25 - elapsedTime); - } catch (InterruptedException ex) { - } - } - } else { - emptySelects = 0; - isClosed = false; - Set keys = selector.selectedKeys(); - synchronized (keys) { - for (Iterator iter = keys.iterator(); iter.hasNext();) { - SelectionKey selectionKey = iter.next(); - iter.remove(); - try { - int ops = selectionKey.readyOps(); - if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ) { - if (selectionKey.attachment() == tcp) { - while (true) { - Object object = tcp.readObject(this); - if (object == null) break; - if (!tcpRegistered) { - if (object instanceof RegisterTCP) { - id = ((RegisterTCP)object).connectionID; - synchronized (tcpRegistrationLock) { - tcpRegistered = true; - tcpRegistrationLock.notifyAll(); - if (TRACE) trace("kryonet", this + " received TCP: RegisterTCP"); - if (udp == null) setConnected(true); - } - if (udp == null) notifyConnected(); - } - continue; - } - if (udp != null && !udpRegistered) { - if (object instanceof RegisterUDP) { - synchronized (udpRegistrationLock) { - udpRegistered = true; - udpRegistrationLock.notifyAll(); - if (TRACE) trace("kryonet", this + " received UDP: RegisterUDP"); - if (DEBUG) { - debug("kryonet", "Port " + udp.datagramChannel.socket().getLocalPort() - + "/UDP connected to: " + udp.connectedAddress); - } - setConnected(true); - } - notifyConnected(); - } - continue; - } - if (!isConnected) continue; - if (DEBUG) { - String objectString = object == null ? "null" : object.getClass().getSimpleName(); - if (!(object instanceof FrameworkMessage)) { - debug("kryonet", this + " received TCP: " + objectString); - } else if (TRACE) { - trace("kryonet", this + " received TCP: " + objectString); - } - } - notifyReceived(object); - } - } else { - if (udp.readFromAddress() == null) continue; - Object object = udp.readObject(this); - if (object == null) continue; - if (DEBUG) { - String objectString = object == null ? "null" : object.getClass().getSimpleName(); - debug("kryonet", this + " received UDP: " + objectString); - } - notifyReceived(object); - } - } - if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) tcp.writeOperation(); - } catch (CancelledKeyException ignored) { - // Connection is closed. - } - } - } - } - if (isConnected) { - long time = System.currentTimeMillis(); - if (tcp.isTimedOut(time)) { - if (DEBUG) debug("kryonet", this + " timed out."); - close(); - } else { - if (tcp.needsKeepAlive(time)) sendTCP(FrameworkMessage.keepAlive); - if (udp != null && udpRegistered && udp.needsKeepAlive(time)) sendUDP(FrameworkMessage.keepAlive); - } - if (isIdle()) notifyIdle(); - } - } - - public void run () { - if (TRACE) trace("kryonet", "Client thread started."); - shutdown = false; - while (!shutdown) { - try { - update(250); - } catch (IOException ex) { - if (TRACE) { - if (isConnected) - trace("kryonet", "Unable to update connection: " + this, ex); - else - trace("kryonet", "Unable to update connection.", ex); - } else if (DEBUG) { - if (isConnected) - debug("kryonet", this + " update: " + ex.getMessage()); - else - debug("kryonet", "Unable to update connection: " + ex.getMessage()); - } - close(); - } catch (KryoNetException ex) { - if (ERROR) { - if (isConnected) - error("kryonet", "Error updating connection: " + this, ex); - else - error("kryonet", "Error updating connection.", ex); - } - close(); - throw ex; - } - } - if (TRACE) trace("kryonet", "Client thread stopped."); - } - - public void start () { - // Try to let any previous update thread stop. - if (updateThread != null) { - shutdown = true; - try { - updateThread.join(5000); - } catch (InterruptedException ignored) { - } - } - updateThread = new Thread(this, "Client"); - updateThread.setDaemon(true); - updateThread.start(); - } - - public void stop () { - if (shutdown) return; - close(); - if (TRACE) trace("kryonet", "Client thread stopping."); - shutdown = true; - selector.wakeup(); - } - - public void close () { - super.close(); - // Select one last time to complete closing the socket. - synchronized (updateLock) { - if (!isClosed) { - isClosed = true; - selector.wakeup(); - try { - selector.selectNow(); - } catch (IOException ignored) { - } - } - } - } - - public void addListener (Listener listener) { - super.addListener(listener); - if (TRACE) trace("kryonet", "Client listener added."); - } - - public void removeListener (Listener listener) { - super.removeListener(listener); - if (TRACE) trace("kryonet", "Client listener removed."); - } - - /** An empty object will be sent if the UDP connection is inactive more than the specified milliseconds. Network hardware may - * keep a translation table of inside to outside IP addresses and a UDP keep alive keeps this table entry from expiring. Set to - * zero to disable. Defaults to 19000. */ - public void setKeepAliveUDP (int keepAliveMillis) { - if (udp == null) throw new IllegalStateException("Not connected via UDP."); - udp.keepAliveMillis = keepAliveMillis; - } - - public Thread getUpdateThread () { - return updateThread; - } - - private void broadcast (int udpPort, DatagramSocket socket) throws IOException { - ByteBuffer dataBuffer = ByteBuffer.allocate(64); - serialization.write(null, dataBuffer, new DiscoverHost()); - dataBuffer.flip(); - byte[] data = new byte[dataBuffer.limit()]; - dataBuffer.get(data); - for (NetworkInterface iface : Collections.list(NetworkInterface.getNetworkInterfaces())) { - for (InetAddress address : Collections.list(iface.getInetAddresses())) { - // Java 1.5 doesn't support getting the subnet mask, so try the two most common. - byte[] ip = address.getAddress(); - ip[3] = -1; // 255.255.255.0 - try { - socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), udpPort)); - } catch (Exception ignored) { - } - ip[2] = -1; // 255.255.0.0 - try { - socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), udpPort)); - } catch (Exception ignored) { - } - } - } - if (DEBUG) debug("kryonet", "Broadcasted host discovery on port: " + udpPort); - } - - /** Broadcasts a UDP message on the LAN to discover any running servers. The address of the first server to respond is returned. - * @param udpPort The UDP port of the server. - * @param timeoutMillis The number of milliseconds to wait for a response. - * @return the first server found, or null if no server responded. */ - public InetAddress discoverHost (int udpPort, int timeoutMillis) { - DatagramSocket socket = null; - try { - socket = new DatagramSocket(); - broadcast(udpPort, socket); - socket.setSoTimeout(timeoutMillis); - DatagramPacket packet = new DatagramPacket(new byte[0], 0); - try { - socket.receive(packet); - } catch (SocketTimeoutException ex) { - if (INFO) info("kryonet", "Host discovery timed out."); - return null; - } - if (INFO) info("kryonet", "Discovered server: " + packet.getAddress()); - return packet.getAddress(); - } catch (IOException ex) { - if (ERROR) error("kryonet", "Host discovery failed.", ex); - return null; - } finally { - if (socket != null) socket.close(); - } - } - - /** Broadcasts a UDP message on the LAN to discover any running servers. - * @param udpPort The UDP port of the server. - * @param timeoutMillis The number of milliseconds to wait for a response. */ - public List discoverHosts (int udpPort, int timeoutMillis) { - List hosts = new ArrayList(); - DatagramSocket socket = null; - try { - socket = new DatagramSocket(); - broadcast(udpPort, socket); - socket.setSoTimeout(timeoutMillis); - while (true) { - DatagramPacket packet = new DatagramPacket(new byte[0], 0); - try { - socket.receive(packet); - } catch (SocketTimeoutException ex) { - if (INFO) info("kryonet", "Host discovery timed out."); - return hosts; - } - if (INFO) info("kryonet", "Discovered server: " + packet.getAddress()); - hosts.add(packet.getAddress()); - } - } catch (IOException ex) { - if (ERROR) error("kryonet", "Host discovery failed.", ex); - return hosts; - } finally { - if (socket != null) socket.close(); - } - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketTimeoutException; +import java.nio.ByteBuffer; +import java.nio.channels.CancelledKeyException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryonet.FrameworkMessage.DiscoverHost; +import com.esotericsoftware.kryonet.FrameworkMessage.RegisterTCP; +import com.esotericsoftware.kryonet.FrameworkMessage.RegisterUDP; + +import static com.esotericsoftware.minlog.Log.*; + +/** Represents a TCP and optionally a UDP connection to a {@link Server}. + * @author Nathan Sweet */ +public class Client extends Connection implements EndPoint { + static { + try { + // Needed for NIO selectors on Android 2.2. + System.setProperty("java.net.preferIPv6Addresses", "false"); + } catch (AccessControlException ignored) { + } + } + + private final Serialization serialization; + private Selector selector; + private int emptySelects; + private volatile boolean tcpRegistered, udpRegistered; + private Object tcpRegistrationLock = new Object(); + private Object udpRegistrationLock = new Object(); + private volatile boolean shutdown; + private final Object updateLock = new Object(); + private Thread updateThread; + private int connectTimeout; + private InetAddress connectHost; + private int connectTcpPort; + private int connectUdpPort; + private boolean isClosed; + + /** Creates a Client with a write buffer size of 8192 and an object buffer size of 2048. */ + public Client () { + this(8192, 2048); + } + + /** @param writeBufferSize One buffer of this size is allocated. Objects are serialized to the write buffer where the bytes are + * queued until they can be written to the TCP socket. + *

    + * Normally the socket is writable and the bytes are written immediately. If the socket cannot be written to and + * enough serialized objects are queued to overflow the buffer, then the connection will be closed. + *

    + * The write buffer should be sized at least as large as the largest object that will be sent, plus some head room to + * allow for some serialized objects to be queued in case the buffer is temporarily not writable. The amount of head + * room needed is dependent upon the size of objects being sent and how often they are sent. + * @param objectBufferSize One (using only TCP) or three (using both TCP and UDP) buffers of this size are allocated. These + * buffers are used to hold the bytes for a single object graph until it can be sent over the network or + * deserialized. + *

    + * The object buffers should be sized at least as large as the largest object that will be sent or received. */ + public Client (int writeBufferSize, int objectBufferSize) { + this(writeBufferSize, objectBufferSize, new KryoSerialization()); + } + + public Client (int writeBufferSize, int objectBufferSize, Serialization serialization) { + super(); + endPoint = this; + + this.serialization = serialization; + + initialize(serialization, writeBufferSize, objectBufferSize); + + try { + selector = Selector.open(); + } catch (IOException ex) { + throw new RuntimeException("Error opening selector.", ex); + } + } + + public Serialization getSerialization () { + return serialization; + } + + public Kryo getKryo () { + return ((KryoSerialization)serialization).getKryo(); + } + + /** Opens a TCP only client. + * @see #connect(int, InetAddress, int, int) */ + public void connect (int timeout, String host, int tcpPort) throws IOException { + connect(timeout, InetAddress.getByName(host), tcpPort, -1); + } + + /** Opens a TCP and UDP client. + * @see #connect(int, InetAddress, int, int) */ + public void connect (int timeout, String host, int tcpPort, int udpPort) throws IOException { + connect(timeout, InetAddress.getByName(host), tcpPort, udpPort); + } + + /** Opens a TCP only client. + * @see #connect(int, InetAddress, int, int) */ + public void connect (int timeout, InetAddress host, int tcpPort) throws IOException { + connect(timeout, host, tcpPort, -1); + } + + /** Opens a TCP and UDP client. Blocks until the connection is complete or the timeout is reached. + *

    + * Because the framework must perform some minimal communication before the connection is considered successful, + * {@link #update(int)} must be called on a separate thread during the connection process. + * @throws IllegalStateException if called from the connection's update thread. + * @throws IOException if the client could not be opened or connecting times out. */ + public void connect (int timeout, InetAddress host, int tcpPort, int udpPort) throws IOException { + if (host == null) throw new IllegalArgumentException("host cannot be null."); + if (Thread.currentThread() == getUpdateThread()) + throw new IllegalStateException("Cannot connect on the connection's update thread."); + this.connectTimeout = timeout; + this.connectHost = host; + this.connectTcpPort = tcpPort; + this.connectUdpPort = udpPort; + close(); + if (INFO) { + if (udpPort != -1) + info("Connecting: " + host + ":" + tcpPort + "/" + udpPort); + else + info("Connecting: " + host + ":" + tcpPort); + } + id = -1; + try { + if (udpPort != -1) udp = new UdpConnection(serialization, tcp.readBuffer.capacity()); + + long endTime; + synchronized (updateLock) { + tcpRegistered = false; + selector.wakeup(); + endTime = System.currentTimeMillis() + timeout; + tcp.connect(selector, new InetSocketAddress(host, tcpPort), 5000); + } + + // Wait for RegisterTCP. + synchronized (tcpRegistrationLock) { + while (!tcpRegistered && System.currentTimeMillis() < endTime) { + try { + tcpRegistrationLock.wait(100); + } catch (InterruptedException ignored) { + } + } + if (!tcpRegistered) { + throw new SocketTimeoutException("Connected, but timed out during TCP registration.\n" + + "Note: Client#update must be called in a separate thread during connect."); + } + } + + if (udpPort != -1) { + InetSocketAddress udpAddress = new InetSocketAddress(host, udpPort); + synchronized (updateLock) { + udpRegistered = false; + selector.wakeup(); + udp.connect(selector, udpAddress); + } + + // Wait for RegisterUDP reply. + synchronized (udpRegistrationLock) { + while (!udpRegistered && System.currentTimeMillis() < endTime) { + RegisterUDP registerUDP = new RegisterUDP(); + registerUDP.connectionID = id; + udp.send(this, registerUDP, udpAddress); + try { + udpRegistrationLock.wait(100); + } catch (InterruptedException ignored) { + } + } + if (!udpRegistered) + throw new SocketTimeoutException("Connected, but timed out during UDP registration: " + host + ":" + udpPort); + } + } + } catch (IOException ex) { + close(); + throw ex; + } + } + + /** Calls {@link #connect(int, InetAddress, int) connect} with the values last passed to connect. + * @throws IllegalStateException if connect has never been called. */ + public void reconnect () throws IOException { + reconnect(connectTimeout); + } + + /** Calls {@link #connect(int, InetAddress, int) connect} with the specified timeout and the other values last passed to + * connect. + * @throws IllegalStateException if connect has never been called. */ + public void reconnect (int timeout) throws IOException { + if (connectHost == null) throw new IllegalStateException("This client has never been connected."); + connect(connectTimeout, connectHost, connectTcpPort, connectUdpPort); + } + + /** Reads or writes any pending data for this client. Multiple threads should not call this method at the same time. + * @param timeout Wait for up to the specified milliseconds for data to be ready to process. May be zero to return immediately + * if there is no data to process. */ + public void update (int timeout) throws IOException { + updateThread = Thread.currentThread(); + synchronized (updateLock) { // Blocks to avoid a select while the selector is used to bind the server connection. + } + long startTime = System.currentTimeMillis(); + int select = 0; + if (timeout > 0) { + select = selector.select(timeout); + } else { + select = selector.selectNow(); + } + if (select == 0) { + emptySelects++; + if (emptySelects == 100) { + emptySelects = 0; + // NIO freaks and returns immediately with 0 sometimes, so try to keep from hogging the CPU. + long elapsedTime = System.currentTimeMillis() - startTime; + try { + if (elapsedTime < 25) Thread.sleep(25 - elapsedTime); + } catch (InterruptedException ex) { + } + } + } else { + emptySelects = 0; + isClosed = false; + Set keys = selector.selectedKeys(); + synchronized (keys) { + for (Iterator iter = keys.iterator(); iter.hasNext();) { + SelectionKey selectionKey = iter.next(); + iter.remove(); + try { + int ops = selectionKey.readyOps(); + if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ) { + if (selectionKey.attachment() == tcp) { + while (true) { + Object object = tcp.readObject(this); + if (object == null) break; + if (!tcpRegistered) { + if (object instanceof RegisterTCP) { + id = ((RegisterTCP)object).connectionID; + synchronized (tcpRegistrationLock) { + tcpRegistered = true; + tcpRegistrationLock.notifyAll(); + if (TRACE) trace("kryonet", this + " received TCP: RegisterTCP"); + if (udp == null) setConnected(true); + } + if (udp == null) notifyConnected(); + } + continue; + } + if (udp != null && !udpRegistered) { + if (object instanceof RegisterUDP) { + synchronized (udpRegistrationLock) { + udpRegistered = true; + udpRegistrationLock.notifyAll(); + if (TRACE) trace("kryonet", this + " received UDP: RegisterUDP"); + if (DEBUG) { + debug("kryonet", "Port " + udp.datagramChannel.socket().getLocalPort() + + "/UDP connected to: " + udp.connectedAddress); + } + setConnected(true); + } + notifyConnected(); + } + continue; + } + if (!isConnected) continue; + if (DEBUG) { + String objectString = object == null ? "null" : object.getClass().getSimpleName(); + if (!(object instanceof FrameworkMessage)) { + debug("kryonet", this + " received TCP: " + objectString); + } else if (TRACE) { + trace("kryonet", this + " received TCP: " + objectString); + } + } + notifyReceived(object); + } + } else { + if (udp.readFromAddress() == null) continue; + Object object = udp.readObject(this); + if (object == null) continue; + if (DEBUG) { + String objectString = object == null ? "null" : object.getClass().getSimpleName(); + debug("kryonet", this + " received UDP: " + objectString); + } + notifyReceived(object); + } + } + if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) tcp.writeOperation(); + } catch (CancelledKeyException ignored) { + // Connection is closed. + } + } + } + } + if (isConnected) { + long time = System.currentTimeMillis(); + if (tcp.isTimedOut(time)) { + if (DEBUG) debug("kryonet", this + " timed out."); + close(); + } else { + if (tcp.needsKeepAlive(time)) sendTCP(FrameworkMessage.keepAlive); + if (udp != null && udpRegistered && udp.needsKeepAlive(time)) sendUDP(FrameworkMessage.keepAlive); + } + if (isIdle()) notifyIdle(); + } + } + + public void run () { + if (TRACE) trace("kryonet", "Client thread started."); + shutdown = false; + while (!shutdown) { + try { + update(250); + } catch (IOException ex) { + if (TRACE) { + if (isConnected) + trace("kryonet", "Unable to update connection: " + this, ex); + else + trace("kryonet", "Unable to update connection.", ex); + } else if (DEBUG) { + if (isConnected) + debug("kryonet", this + " update: " + ex.getMessage()); + else + debug("kryonet", "Unable to update connection: " + ex.getMessage()); + } + close(); + } catch (KryoNetException ex) { + if (ERROR) { + if (isConnected) + error("kryonet", "Error updating connection: " + this, ex); + else + error("kryonet", "Error updating connection.", ex); + } + close(); + throw ex; + } + } + if (TRACE) trace("kryonet", "Client thread stopped."); + } + + public void start () { + // Try to let any previous update thread stop. + if (updateThread != null) { + shutdown = true; + try { + updateThread.join(5000); + } catch (InterruptedException ignored) { + } + } + updateThread = new Thread(this, "Client"); + updateThread.setDaemon(true); + updateThread.start(); + } + + public void stop () { + if (shutdown) return; + close(); + if (TRACE) trace("kryonet", "Client thread stopping."); + shutdown = true; + selector.wakeup(); + } + + public void close () { + super.close(); + // Select one last time to complete closing the socket. + synchronized (updateLock) { + if (!isClosed) { + isClosed = true; + selector.wakeup(); + try { + selector.selectNow(); + } catch (IOException ignored) { + } + } + } + } + + public void addListener (Listener listener) { + super.addListener(listener); + if (TRACE) trace("kryonet", "Client listener added."); + } + + public void removeListener (Listener listener) { + super.removeListener(listener); + if (TRACE) trace("kryonet", "Client listener removed."); + } + + /** An empty object will be sent if the UDP connection is inactive more than the specified milliseconds. Network hardware may + * keep a translation table of inside to outside IP addresses and a UDP keep alive keeps this table entry from expiring. Set to + * zero to disable. Defaults to 19000. */ + public void setKeepAliveUDP (int keepAliveMillis) { + if (udp == null) throw new IllegalStateException("Not connected via UDP."); + udp.keepAliveMillis = keepAliveMillis; + } + + public Thread getUpdateThread () { + return updateThread; + } + + private void broadcast (int udpPort, DatagramSocket socket) throws IOException { + ByteBuffer dataBuffer = ByteBuffer.allocate(64); + serialization.write(null, dataBuffer, new DiscoverHost()); + dataBuffer.flip(); + byte[] data = new byte[dataBuffer.limit()]; + dataBuffer.get(data); + for (NetworkInterface iface : Collections.list(NetworkInterface.getNetworkInterfaces())) { + for (InetAddress address : Collections.list(iface.getInetAddresses())) { + // Java 1.5 doesn't support getting the subnet mask, so try the two most common. + byte[] ip = address.getAddress(); + ip[3] = -1; // 255.255.255.0 + try { + socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), udpPort)); + } catch (Exception ignored) { + } + ip[2] = -1; // 255.255.0.0 + try { + socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), udpPort)); + } catch (Exception ignored) { + } + } + } + if (DEBUG) debug("kryonet", "Broadcasted host discovery on port: " + udpPort); + } + + /** Broadcasts a UDP message on the LAN to discover any running servers. The address of the first server to respond is returned. + * @param udpPort The UDP port of the server. + * @param timeoutMillis The number of milliseconds to wait for a response. + * @return the first server found, or null if no server responded. */ + public InetAddress discoverHost (int udpPort, int timeoutMillis) { + DatagramSocket socket = null; + try { + socket = new DatagramSocket(); + broadcast(udpPort, socket); + socket.setSoTimeout(timeoutMillis); + DatagramPacket packet = new DatagramPacket(new byte[0], 0); + try { + socket.receive(packet); + } catch (SocketTimeoutException ex) { + if (INFO) info("kryonet", "Host discovery timed out."); + return null; + } + if (INFO) info("kryonet", "Discovered server: " + packet.getAddress()); + return packet.getAddress(); + } catch (IOException ex) { + if (ERROR) error("kryonet", "Host discovery failed.", ex); + return null; + } finally { + if (socket != null) socket.close(); + } + } + + /** Broadcasts a UDP message on the LAN to discover any running servers. + * @param udpPort The UDP port of the server. + * @param timeoutMillis The number of milliseconds to wait for a response. */ + public List discoverHosts (int udpPort, int timeoutMillis) { + List hosts = new ArrayList(); + DatagramSocket socket = null; + try { + socket = new DatagramSocket(); + broadcast(udpPort, socket); + socket.setSoTimeout(timeoutMillis); + while (true) { + DatagramPacket packet = new DatagramPacket(new byte[0], 0); + try { + socket.receive(packet); + } catch (SocketTimeoutException ex) { + if (INFO) info("kryonet", "Host discovery timed out."); + return hosts; + } + if (INFO) info("kryonet", "Discovered server: " + packet.getAddress()); + hosts.add(packet.getAddress()); + } + } catch (IOException ex) { + if (ERROR) error("kryonet", "Host discovery failed.", ex); + return hosts; + } finally { + if (socket != null) socket.close(); + } + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/Connection.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/Connection.java similarity index 97% rename from kryonet/src/com/esotericsoftware/kryonet/Connection.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/Connection.java index 83f386b4..3296f286 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/Connection.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/Connection.java @@ -1,312 +1,312 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.SocketAddress; -import java.net.SocketException; -import java.nio.channels.SocketChannel; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryonet.FrameworkMessage.Ping; - -import static com.esotericsoftware.minlog.Log.*; - -// BOZO - Layer to handle handshake state. - -/** Represents a TCP and optionally a UDP connection between a {@link Client} and a {@link Server}. If either underlying connection - * is closed or errors, both connections are closed. - * @author Nathan Sweet */ -public class Connection { - int id = -1; - private String name; - EndPoint endPoint; - TcpConnection tcp; - UdpConnection udp; - InetSocketAddress udpRemoteAddress; - private Listener[] listeners = {}; - private Object listenerLock = new Object(); - private int lastPingID; - private long lastPingSendTime; - private int returnTripTime; - volatile boolean isConnected; - - protected Connection () { - } - - void initialize (Serialization serialization, int writeBufferSize, int objectBufferSize) { - tcp = new TcpConnection(serialization, writeBufferSize, objectBufferSize); - } - - /** Returns the server assigned ID. Will return -1 if this connection has never been connected or the last assigned ID if this - * connection has been disconnected. */ - public int getID () { - return id; - } - - /** Returns true if this connection is connected to the remote end. Note that a connection can become disconnected at any time. */ - public boolean isConnected () { - return isConnected; - } - - /** Sends the object over the network using TCP. - * @return The number of bytes sent. - * @see Kryo#register(Class, com.esotericsoftware.kryo.Serializer) */ - public int sendTCP (Object object) { - if (object == null) throw new IllegalArgumentException("object cannot be null."); - try { - int length = tcp.send(this, object); - if (length == 0) { - if (TRACE) trace("kryonet", this + " TCP had nothing to send."); - } else if (DEBUG) { - String objectString = object == null ? "null" : object.getClass().getSimpleName(); - if (!(object instanceof FrameworkMessage)) { - debug("kryonet", this + " sent TCP: " + objectString + " (" + length + ")"); - } else if (TRACE) { - trace("kryonet", this + " sent TCP: " + objectString + " (" + length + ")"); - } - } - return length; - } catch (IOException ex) { - if (DEBUG) debug("kryonet", "Unable to send TCP with connection: " + this, ex); - close(); - return 0; - } catch (KryoNetException ex) { - if (ERROR) error("kryonet", "Unable to send TCP with connection: " + this, ex); - close(); - return 0; - } - } - - /** Sends the object over the network using UDP. - * @return The number of bytes sent. - * @see Kryo#register(Class, com.esotericsoftware.kryo.Serializer) - * @throws IllegalStateException if this connection was not opened with both TCP and UDP. */ - public int sendUDP (Object object) { - if (object == null) throw new IllegalArgumentException("object cannot be null."); - SocketAddress address = udpRemoteAddress; - if (address == null && udp != null) address = udp.connectedAddress; - if (address == null && isConnected) throw new IllegalStateException("Connection is not connected via UDP."); - - try { - if (address == null) throw new SocketException("Connection is closed."); - - int length = udp.send(this, object, address); - if (length == 0) { - if (TRACE) trace("kryonet", this + " UDP had nothing to send."); - } else if (DEBUG) { - if (length != -1) { - String objectString = object == null ? "null" : object.getClass().getSimpleName(); - if (!(object instanceof FrameworkMessage)) { - debug("kryonet", this + " sent UDP: " + objectString + " (" + length + ")"); - } else if (TRACE) { - trace("kryonet", this + " sent UDP: " + objectString + " (" + length + ")"); - } - } else - debug("kryonet", this + " was unable to send, UDP socket buffer full."); - } - return length; - } catch (IOException ex) { - if (DEBUG) debug("kryonet", "Unable to send UDP with connection: " + this, ex); - close(); - return 0; - } catch (KryoNetException ex) { - if (ERROR) error("kryonet", "Unable to send UDP with connection: " + this, ex); - close(); - return 0; - } - } - - public void close () { - boolean wasConnected = isConnected; - isConnected = false; - tcp.close(); - if (udp != null && udp.connectedAddress != null) udp.close(); - if (wasConnected) { - notifyDisconnected(); - if (INFO) info("kryonet", this + " disconnected."); - } - setConnected(false); - } - - /** Requests the connection to communicate with the remote computer to determine a new value for the - * {@link #getReturnTripTime() return trip time}. When the connection receives a {@link FrameworkMessage.Ping} object with - * {@link Ping#isReply isReply} set to true, the new return trip time is available. */ - public void updateReturnTripTime () { - Ping ping = new Ping(); - ping.id = lastPingID++; - lastPingSendTime = System.currentTimeMillis(); - sendTCP(ping); - } - - /** Returns the last calculated TCP return trip time, or -1 if {@link #updateReturnTripTime()} has never been called or the - * {@link FrameworkMessage.Ping} response has not yet been received. */ - public int getReturnTripTime () { - return returnTripTime; - } - - /** An empty object will be sent if the TCP connection has not sent an object within the specified milliseconds. Periodically - * sending a keep alive ensures that an abnormal close is detected in a reasonable amount of time (see {@link #setTimeout(int)} - * ). Also, some network hardware will close a TCP connection that ceases to transmit for a period of time (typically 1+ - * minutes). Set to zero to disable. Defaults to 8000. */ - public void setKeepAliveTCP (int keepAliveMillis) { - tcp.keepAliveMillis = keepAliveMillis; - } - - /** If the specified amount of time passes without receiving an object over TCP, the connection is considered closed. When a TCP - * socket is closed normally, the remote end is notified immediately and this timeout is not needed. However, if a socket is - * closed abnormally (eg, power loss), KryoNet uses this timeout to detect the problem. The timeout should be set higher than - * the {@link #setKeepAliveTCP(int) TCP keep alive} for the remote end of the connection. The keep alive ensures that the remote - * end of the connection will be constantly sending objects, and setting the timeout higher than the keep alive allows for - * network latency. Set to zero to disable. Defaults to 12000. */ - public void setTimeout (int timeoutMillis) { - tcp.timeoutMillis = timeoutMillis; - } - - /** If the listener already exists, it is not added again. */ - public void addListener (Listener listener) { - if (listener == null) throw new IllegalArgumentException("listener cannot be null."); - synchronized (listenerLock) { - Listener[] listeners = this.listeners; - int n = listeners.length; - for (int i = 0; i < n; i++) - if (listener == listeners[i]) return; - Listener[] newListeners = new Listener[n + 1]; - newListeners[0] = listener; - System.arraycopy(listeners, 0, newListeners, 1, n); - this.listeners = newListeners; - } - if (TRACE) trace("kryonet", "Connection listener added: " + listener.getClass().getName()); - } - - public void removeListener (Listener listener) { - if (listener == null) throw new IllegalArgumentException("listener cannot be null."); - synchronized (listenerLock) { - Listener[] listeners = this.listeners; - int n = listeners.length; - if (n == 0) return; - Listener[] newListeners = new Listener[n - 1]; - for (int i = 0, ii = 0; i < n; i++) { - Listener copyListener = listeners[i]; - if (listener == copyListener) continue; - if (ii == n - 1) return; - newListeners[ii++] = copyListener; - } - this.listeners = newListeners; - } - if (TRACE) trace("kryonet", "Connection listener removed: " + listener.getClass().getName()); - } - - void notifyConnected () { - if (INFO) { - SocketChannel socketChannel = tcp.socketChannel; - if (socketChannel != null) { - Socket socket = tcp.socketChannel.socket(); - if (socket != null) { - InetSocketAddress remoteSocketAddress = (InetSocketAddress)socket.getRemoteSocketAddress(); - if (remoteSocketAddress != null) info("kryonet", this + " connected: " + remoteSocketAddress.getAddress()); - } - } - } - Listener[] listeners = this.listeners; - for (int i = 0, n = listeners.length; i < n; i++) - listeners[i].connected(this); - } - - void notifyDisconnected () { - Listener[] listeners = this.listeners; - for (int i = 0, n = listeners.length; i < n; i++) - listeners[i].disconnected(this); - } - - void notifyIdle () { - Listener[] listeners = this.listeners; - for (int i = 0, n = listeners.length; i < n; i++) { - listeners[i].idle(this); - if (!isIdle()) break; - } - } - - void notifyReceived (Object object) { - if (object instanceof Ping) { - Ping ping = (Ping)object; - if (ping.isReply) { - if (ping.id == lastPingID - 1) { - returnTripTime = (int)(System.currentTimeMillis() - lastPingSendTime); - if (TRACE) trace("kryonet", this + " return trip time: " + returnTripTime); - } - } else { - ping.isReply = true; - sendTCP(ping); - } - } - Listener[] listeners = this.listeners; - for (int i = 0, n = listeners.length; i < n; i++) - listeners[i].received(this, object); - } - - /** Returns the local {@link Client} or {@link Server} to which this connection belongs. */ - public EndPoint getEndPoint () { - return endPoint; - } - - /** Returns the IP address and port of the remote end of the TCP connection, or null if this connection is not connected. */ - public InetSocketAddress getRemoteAddressTCP () { - SocketChannel socketChannel = tcp.socketChannel; - if (socketChannel != null) { - Socket socket = tcp.socketChannel.socket(); - if (socket != null) { - return (InetSocketAddress)socket.getRemoteSocketAddress(); - } - } - return null; - } - - /** Returns the IP address and port of the remote end of the UDP connection, or null if this connection is not connected. */ - public InetSocketAddress getRemoteAddressUDP () { - InetSocketAddress connectedAddress = udp.connectedAddress; - if (connectedAddress != null) return connectedAddress; - return udpRemoteAddress; - } - - /** Workaround for broken NIO networking on Android 1.6. If true, the underlying NIO buffer is always copied to the beginning of - * the buffer before being given to the SocketChannel for sending. The Harmony SocketChannel implementation in Android 1.6 - * ignores the buffer position, always copying from the beginning of the buffer. This is fixed in Android 2.0+. */ - public void setBufferPositionFix (boolean bufferPositionFix) { - tcp.bufferPositionFix = bufferPositionFix; - } - - /** Sets the friendly name of this connection. This is returned by {@link #toString()} and is useful for providing application - * specific identifying information in the logging. May be null for the default name of "Connection X", where X is the - * connection ID. */ - public void setName (String name) { - this.name = name; - } - - /** Returns the number of bytes that are waiting to be written to the TCP socket, if any. */ - public int getTcpWriteBufferSize () { - return tcp.writeBuffer.position(); - } - - /** @see #setIdleThreshold(float) */ - public boolean isIdle () { - return tcp.writeBuffer.position() / (float)tcp.writeBuffer.capacity() < tcp.idleThreshold; - } - - /** If the percent of the TCP write buffer that is filled is less than the specified threshold, - * {@link Listener#idle(Connection)} will be called for each network thread update. Default is 0.1. */ - public void setIdleThreshold (float idleThreshold) { - tcp.idleThreshold = idleThreshold; - } - - public String toString () { - if (name != null) return name; - return "Connection " + id; - } - - void setConnected (boolean isConnected) { - this.isConnected = isConnected; - if (isConnected && name == null) name = "Connection " + id; - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.SocketChannel; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryonet.FrameworkMessage.Ping; + +import static com.esotericsoftware.minlog.Log.*; + +// BOZO - Layer to handle handshake state. + +/** Represents a TCP and optionally a UDP connection between a {@link Client} and a {@link Server}. If either underlying connection + * is closed or errors, both connections are closed. + * @author Nathan Sweet */ +public class Connection { + int id = -1; + private String name; + EndPoint endPoint; + TcpConnection tcp; + UdpConnection udp; + InetSocketAddress udpRemoteAddress; + private Listener[] listeners = {}; + private Object listenerLock = new Object(); + private int lastPingID; + private long lastPingSendTime; + private int returnTripTime; + volatile boolean isConnected; + + protected Connection () { + } + + void initialize (Serialization serialization, int writeBufferSize, int objectBufferSize) { + tcp = new TcpConnection(serialization, writeBufferSize, objectBufferSize); + } + + /** Returns the server assigned ID. Will return -1 if this connection has never been connected or the last assigned ID if this + * connection has been disconnected. */ + public int getID () { + return id; + } + + /** Returns true if this connection is connected to the remote end. Note that a connection can become disconnected at any time. */ + public boolean isConnected () { + return isConnected; + } + + /** Sends the object over the network using TCP. + * @return The number of bytes sent. + * @see Kryo#register(Class, com.esotericsoftware.kryo.Serializer) */ + public int sendTCP (Object object) { + if (object == null) throw new IllegalArgumentException("object cannot be null."); + try { + int length = tcp.send(this, object); + if (length == 0) { + if (TRACE) trace("kryonet", this + " TCP had nothing to send."); + } else if (DEBUG) { + String objectString = object == null ? "null" : object.getClass().getSimpleName(); + if (!(object instanceof FrameworkMessage)) { + debug("kryonet", this + " sent TCP: " + objectString + " (" + length + ")"); + } else if (TRACE) { + trace("kryonet", this + " sent TCP: " + objectString + " (" + length + ")"); + } + } + return length; + } catch (IOException ex) { + if (DEBUG) debug("kryonet", "Unable to send TCP with connection: " + this, ex); + close(); + return 0; + } catch (KryoNetException ex) { + if (ERROR) error("kryonet", "Unable to send TCP with connection: " + this, ex); + close(); + return 0; + } + } + + /** Sends the object over the network using UDP. + * @return The number of bytes sent. + * @see Kryo#register(Class, com.esotericsoftware.kryo.Serializer) + * @throws IllegalStateException if this connection was not opened with both TCP and UDP. */ + public int sendUDP (Object object) { + if (object == null) throw new IllegalArgumentException("object cannot be null."); + SocketAddress address = udpRemoteAddress; + if (address == null && udp != null) address = udp.connectedAddress; + if (address == null && isConnected) throw new IllegalStateException("Connection is not connected via UDP."); + + try { + if (address == null) throw new SocketException("Connection is closed."); + + int length = udp.send(this, object, address); + if (length == 0) { + if (TRACE) trace("kryonet", this + " UDP had nothing to send."); + } else if (DEBUG) { + if (length != -1) { + String objectString = object == null ? "null" : object.getClass().getSimpleName(); + if (!(object instanceof FrameworkMessage)) { + debug("kryonet", this + " sent UDP: " + objectString + " (" + length + ")"); + } else if (TRACE) { + trace("kryonet", this + " sent UDP: " + objectString + " (" + length + ")"); + } + } else + debug("kryonet", this + " was unable to send, UDP socket buffer full."); + } + return length; + } catch (IOException ex) { + if (DEBUG) debug("kryonet", "Unable to send UDP with connection: " + this, ex); + close(); + return 0; + } catch (KryoNetException ex) { + if (ERROR) error("kryonet", "Unable to send UDP with connection: " + this, ex); + close(); + return 0; + } + } + + public void close () { + boolean wasConnected = isConnected; + isConnected = false; + tcp.close(); + if (udp != null && udp.connectedAddress != null) udp.close(); + if (wasConnected) { + notifyDisconnected(); + if (INFO) info("kryonet", this + " disconnected."); + } + setConnected(false); + } + + /** Requests the connection to communicate with the remote computer to determine a new value for the + * {@link #getReturnTripTime() return trip time}. When the connection receives a {@link FrameworkMessage.Ping} object with + * {@link Ping#isReply isReply} set to true, the new return trip time is available. */ + public void updateReturnTripTime () { + Ping ping = new Ping(); + ping.id = lastPingID++; + lastPingSendTime = System.currentTimeMillis(); + sendTCP(ping); + } + + /** Returns the last calculated TCP return trip time, or -1 if {@link #updateReturnTripTime()} has never been called or the + * {@link FrameworkMessage.Ping} response has not yet been received. */ + public int getReturnTripTime () { + return returnTripTime; + } + + /** An empty object will be sent if the TCP connection has not sent an object within the specified milliseconds. Periodically + * sending a keep alive ensures that an abnormal close is detected in a reasonable amount of time (see {@link #setTimeout(int)} + * ). Also, some network hardware will close a TCP connection that ceases to transmit for a period of time (typically 1+ + * minutes). Set to zero to disable. Defaults to 8000. */ + public void setKeepAliveTCP (int keepAliveMillis) { + tcp.keepAliveMillis = keepAliveMillis; + } + + /** If the specified amount of time passes without receiving an object over TCP, the connection is considered closed. When a TCP + * socket is closed normally, the remote end is notified immediately and this timeout is not needed. However, if a socket is + * closed abnormally (eg, power loss), KryoNet uses this timeout to detect the problem. The timeout should be set higher than + * the {@link #setKeepAliveTCP(int) TCP keep alive} for the remote end of the connection. The keep alive ensures that the remote + * end of the connection will be constantly sending objects, and setting the timeout higher than the keep alive allows for + * network latency. Set to zero to disable. Defaults to 12000. */ + public void setTimeout (int timeoutMillis) { + tcp.timeoutMillis = timeoutMillis; + } + + /** If the listener already exists, it is not added again. */ + public void addListener (Listener listener) { + if (listener == null) throw new IllegalArgumentException("listener cannot be null."); + synchronized (listenerLock) { + Listener[] listeners = this.listeners; + int n = listeners.length; + for (int i = 0; i < n; i++) + if (listener == listeners[i]) return; + Listener[] newListeners = new Listener[n + 1]; + newListeners[0] = listener; + System.arraycopy(listeners, 0, newListeners, 1, n); + this.listeners = newListeners; + } + if (TRACE) trace("kryonet", "Connection listener added: " + listener.getClass().getName()); + } + + public void removeListener (Listener listener) { + if (listener == null) throw new IllegalArgumentException("listener cannot be null."); + synchronized (listenerLock) { + Listener[] listeners = this.listeners; + int n = listeners.length; + if (n == 0) return; + Listener[] newListeners = new Listener[n - 1]; + for (int i = 0, ii = 0; i < n; i++) { + Listener copyListener = listeners[i]; + if (listener == copyListener) continue; + if (ii == n - 1) return; + newListeners[ii++] = copyListener; + } + this.listeners = newListeners; + } + if (TRACE) trace("kryonet", "Connection listener removed: " + listener.getClass().getName()); + } + + void notifyConnected () { + if (INFO) { + SocketChannel socketChannel = tcp.socketChannel; + if (socketChannel != null) { + Socket socket = tcp.socketChannel.socket(); + if (socket != null) { + InetSocketAddress remoteSocketAddress = (InetSocketAddress)socket.getRemoteSocketAddress(); + if (remoteSocketAddress != null) info("kryonet", this + " connected: " + remoteSocketAddress.getAddress()); + } + } + } + Listener[] listeners = this.listeners; + for (int i = 0, n = listeners.length; i < n; i++) + listeners[i].connected(this); + } + + void notifyDisconnected () { + Listener[] listeners = this.listeners; + for (int i = 0, n = listeners.length; i < n; i++) + listeners[i].disconnected(this); + } + + void notifyIdle () { + Listener[] listeners = this.listeners; + for (int i = 0, n = listeners.length; i < n; i++) { + listeners[i].idle(this); + if (!isIdle()) break; + } + } + + void notifyReceived (Object object) { + if (object instanceof Ping) { + Ping ping = (Ping)object; + if (ping.isReply) { + if (ping.id == lastPingID - 1) { + returnTripTime = (int)(System.currentTimeMillis() - lastPingSendTime); + if (TRACE) trace("kryonet", this + " return trip time: " + returnTripTime); + } + } else { + ping.isReply = true; + sendTCP(ping); + } + } + Listener[] listeners = this.listeners; + for (int i = 0, n = listeners.length; i < n; i++) + listeners[i].received(this, object); + } + + /** Returns the local {@link Client} or {@link Server} to which this connection belongs. */ + public EndPoint getEndPoint () { + return endPoint; + } + + /** Returns the IP address and port of the remote end of the TCP connection, or null if this connection is not connected. */ + public InetSocketAddress getRemoteAddressTCP () { + SocketChannel socketChannel = tcp.socketChannel; + if (socketChannel != null) { + Socket socket = tcp.socketChannel.socket(); + if (socket != null) { + return (InetSocketAddress)socket.getRemoteSocketAddress(); + } + } + return null; + } + + /** Returns the IP address and port of the remote end of the UDP connection, or null if this connection is not connected. */ + public InetSocketAddress getRemoteAddressUDP () { + InetSocketAddress connectedAddress = udp.connectedAddress; + if (connectedAddress != null) return connectedAddress; + return udpRemoteAddress; + } + + /** Workaround for broken NIO networking on Android 1.6. If true, the underlying NIO buffer is always copied to the beginning of + * the buffer before being given to the SocketChannel for sending. The Harmony SocketChannel implementation in Android 1.6 + * ignores the buffer position, always copying from the beginning of the buffer. This is fixed in Android 2.0+. */ + public void setBufferPositionFix (boolean bufferPositionFix) { + tcp.bufferPositionFix = bufferPositionFix; + } + + /** Sets the friendly name of this connection. This is returned by {@link #toString()} and is useful for providing application + * specific identifying information in the logging. May be null for the default name of "Connection X", where X is the + * connection ID. */ + public void setName (String name) { + this.name = name; + } + + /** Returns the number of bytes that are waiting to be written to the TCP socket, if any. */ + public int getTcpWriteBufferSize () { + return tcp.writeBuffer.position(); + } + + /** @see #setIdleThreshold(float) */ + public boolean isIdle () { + return tcp.writeBuffer.position() / (float)tcp.writeBuffer.capacity() < tcp.idleThreshold; + } + + /** If the percent of the TCP write buffer that is filled is less than the specified threshold, + * {@link Listener#idle(Connection)} will be called for each network thread update. Default is 0.1. */ + public void setIdleThreshold (float idleThreshold) { + tcp.idleThreshold = idleThreshold; + } + + public String toString () { + if (name != null) return name; + return "Connection " + id; + } + + void setConnected (boolean isConnected) { + this.isConnected = isConnected; + if (isConnected && name == null) name = "Connection " + id; + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/EndPoint.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/EndPoint.java similarity index 97% rename from kryonet/src/com/esotericsoftware/kryonet/EndPoint.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/EndPoint.java index 16b2b8b8..b829dd79 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/EndPoint.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/EndPoint.java @@ -1,43 +1,43 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; - -import com.esotericsoftware.kryo.Kryo; - -/** Represents the local end point of a connection. - * @author Nathan Sweet */ -public interface EndPoint extends Runnable { - /** Gets the serialization instance that will be used to serialize and deserialize objects. */ - public Serialization getSerialization (); - - /** If the listener already exists, it is not added again. */ - public void addListener (Listener listener); - - public void removeListener (Listener listener); - - /** Continually updates this end point until {@link #stop()} is called. */ - public void run (); - - /** Starts a new thread that calls {@link #run()}. */ - public void start (); - - /** Closes this end point and causes {@link #run()} to return. */ - public void stop (); - - /** @see Client - * @see Server */ - public void close (); - - /** @see Client#update(int) - * @see Server#update(int) */ - public void update (int timeout) throws IOException; - - /** Returns the last thread that called {@link #update(int)} for this end point. This can be useful to detect when long running - * code will be run on the update thread. */ - public Thread getUpdateThread (); - - /** Gets the Kryo instance that will be used to serialize and deserialize objects. This is only valid if - * {@link KryoSerialization} is being used, which is the default. */ - public Kryo getKryo (); -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; + +import com.esotericsoftware.kryo.Kryo; + +/** Represents the local end point of a connection. + * @author Nathan Sweet */ +public interface EndPoint extends Runnable { + /** Gets the serialization instance that will be used to serialize and deserialize objects. */ + public Serialization getSerialization (); + + /** If the listener already exists, it is not added again. */ + public void addListener (Listener listener); + + public void removeListener (Listener listener); + + /** Continually updates this end point until {@link #stop()} is called. */ + public void run (); + + /** Starts a new thread that calls {@link #run()}. */ + public void start (); + + /** Closes this end point and causes {@link #run()} to return. */ + public void stop (); + + /** @see Client + * @see Server */ + public void close (); + + /** @see Client#update(int) + * @see Server#update(int) */ + public void update (int timeout) throws IOException; + + /** Returns the last thread that called {@link #update(int)} for this end point. This can be useful to detect when long running + * code will be run on the update thread. */ + public Thread getUpdateThread (); + + /** Gets the Kryo instance that will be used to serialize and deserialize objects. This is only valid if + * {@link KryoSerialization} is being used, which is the default. */ + public Kryo getKryo (); +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/FrameworkMessage.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/FrameworkMessage.java similarity index 97% rename from kryonet/src/com/esotericsoftware/kryonet/FrameworkMessage.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/FrameworkMessage.java index 089753a8..aed40c17 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/FrameworkMessage.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/FrameworkMessage.java @@ -1,35 +1,35 @@ - -package com.esotericsoftware.kryonet; - -import com.esotericsoftware.minlog.Log; - -/** Marker interface to denote that a message is used by the Ninja framework and is generally invisible to the developer. Eg, these - * messages are only logged at the {@link Log#LEVEL_TRACE} level. - * @author Nathan Sweet */ -public interface FrameworkMessage { - static final FrameworkMessage.KeepAlive keepAlive = new KeepAlive(); - - /** Internal message to give the client the server assigned connection ID. */ - static public class RegisterTCP implements FrameworkMessage { - public int connectionID; - } - - /** Internal message to give the server the client's UDP port. */ - static public class RegisterUDP implements FrameworkMessage { - public int connectionID; - } - - /** Internal message to keep connections alive. */ - static public class KeepAlive implements FrameworkMessage { - } - - /** Internal message to discover running servers. */ - static public class DiscoverHost implements FrameworkMessage { - } - - /** Internal message to determine round trip time. */ - static public class Ping implements FrameworkMessage { - public int id; - public boolean isReply; - } -} + +package com.esotericsoftware.kryonet; + +import com.esotericsoftware.minlog.Log; + +/** Marker interface to denote that a message is used by the Ninja framework and is generally invisible to the developer. Eg, these + * messages are only logged at the {@link Log#LEVEL_TRACE} level. + * @author Nathan Sweet */ +public interface FrameworkMessage { + static final FrameworkMessage.KeepAlive keepAlive = new KeepAlive(); + + /** Internal message to give the client the server assigned connection ID. */ + static public class RegisterTCP implements FrameworkMessage { + public int connectionID; + } + + /** Internal message to give the server the client's UDP port. */ + static public class RegisterUDP implements FrameworkMessage { + public int connectionID; + } + + /** Internal message to keep connections alive. */ + static public class KeepAlive implements FrameworkMessage { + } + + /** Internal message to discover running servers. */ + static public class DiscoverHost implements FrameworkMessage { + } + + /** Internal message to determine round trip time. */ + static public class Ping implements FrameworkMessage { + public int id; + public boolean isReply; + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/JsonSerialization.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/JsonSerialization.java similarity index 97% rename from kryonet/src/com/esotericsoftware/kryonet/JsonSerialization.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/JsonSerialization.java index ff6f3e38..ce8233c1 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/JsonSerialization.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/JsonSerialization.java @@ -1,82 +1,82 @@ - -package com.esotericsoftware.kryonet; - -import java.io.OutputStreamWriter; -import java.nio.ByteBuffer; - -import com.esotericsoftware.jsonbeans.Json; -import com.esotericsoftware.jsonbeans.JsonException; -import com.esotericsoftware.kryo.io.ByteBufferInputStream; -import com.esotericsoftware.kryo.io.ByteBufferOutputStream; -import com.esotericsoftware.kryonet.FrameworkMessage.DiscoverHost; -import com.esotericsoftware.kryonet.FrameworkMessage.KeepAlive; -import com.esotericsoftware.kryonet.FrameworkMessage.Ping; -import com.esotericsoftware.kryonet.FrameworkMessage.RegisterTCP; -import com.esotericsoftware.kryonet.FrameworkMessage.RegisterUDP; - -import static com.esotericsoftware.minlog.Log.*; - -public class JsonSerialization implements Serialization { - private final Json json = new Json(); - private final ByteBufferInputStream byteBufferInputStream = new ByteBufferInputStream(); - private final ByteBufferOutputStream byteBufferOutputStream = new ByteBufferOutputStream(); - private final OutputStreamWriter writer = new OutputStreamWriter(byteBufferOutputStream); - private boolean logging = true, prettyPrint = true; - private byte[] logBuffer = {}; - - public JsonSerialization () { - json.addClassTag("RegisterTCP", RegisterTCP.class); - json.addClassTag("RegisterUDP", RegisterUDP.class); - json.addClassTag("KeepAlive", KeepAlive.class); - json.addClassTag("DiscoverHost", DiscoverHost.class); - json.addClassTag("Ping", Ping.class); - - json.setWriter(writer); - } - - public void setLogging (boolean logging, boolean prettyPrint) { - this.logging = logging; - this.prettyPrint = prettyPrint; - } - - public void write (Connection connection, ByteBuffer buffer, Object object) { - byteBufferOutputStream.setByteBuffer(buffer); - int start = buffer.position(); - try { - json.writeValue(object, Object.class, null); - writer.flush(); - } catch (Exception ex) { - throw new JsonException("Error writing object: " + object, ex); - } - if (INFO && logging) { - int end = buffer.position(); - buffer.position(start); - buffer.limit(end); - int length = end - start; - if (logBuffer.length < length) logBuffer = new byte[length]; - buffer.get(logBuffer, 0, length); - buffer.position(end); - buffer.limit(buffer.capacity()); - String message = new String(logBuffer, 0, length); - if (prettyPrint) message = json.prettyPrint(message); - info("Wrote: " + message); - } - } - - public Object read (Connection connection, ByteBuffer buffer) { - byteBufferInputStream.setByteBuffer(buffer); - return json.fromJson(Object.class, byteBufferInputStream); - } - - public void writeLength (ByteBuffer buffer, int length) { - buffer.putInt(length); - } - - public int readLength (ByteBuffer buffer) { - return buffer.getInt(); - } - - public int getLengthLength () { - return 4; - } -} + +package com.esotericsoftware.kryonet; + +import java.io.OutputStreamWriter; +import java.nio.ByteBuffer; + +import com.esotericsoftware.jsonbeans.Json; +import com.esotericsoftware.jsonbeans.JsonException; +import com.esotericsoftware.kryo.io.ByteBufferInputStream; +import com.esotericsoftware.kryo.io.ByteBufferOutputStream; +import com.esotericsoftware.kryonet.FrameworkMessage.DiscoverHost; +import com.esotericsoftware.kryonet.FrameworkMessage.KeepAlive; +import com.esotericsoftware.kryonet.FrameworkMessage.Ping; +import com.esotericsoftware.kryonet.FrameworkMessage.RegisterTCP; +import com.esotericsoftware.kryonet.FrameworkMessage.RegisterUDP; + +import static com.esotericsoftware.minlog.Log.*; + +public class JsonSerialization implements Serialization { + private final Json json = new Json(); + private final ByteBufferInputStream byteBufferInputStream = new ByteBufferInputStream(); + private final ByteBufferOutputStream byteBufferOutputStream = new ByteBufferOutputStream(); + private final OutputStreamWriter writer = new OutputStreamWriter(byteBufferOutputStream); + private boolean logging = true, prettyPrint = true; + private byte[] logBuffer = {}; + + public JsonSerialization () { + json.addClassTag("RegisterTCP", RegisterTCP.class); + json.addClassTag("RegisterUDP", RegisterUDP.class); + json.addClassTag("KeepAlive", KeepAlive.class); + json.addClassTag("DiscoverHost", DiscoverHost.class); + json.addClassTag("Ping", Ping.class); + + json.setWriter(writer); + } + + public void setLogging (boolean logging, boolean prettyPrint) { + this.logging = logging; + this.prettyPrint = prettyPrint; + } + + public void write (Connection connection, ByteBuffer buffer, Object object) { + byteBufferOutputStream.setByteBuffer(buffer); + int start = buffer.position(); + try { + json.writeValue(object, Object.class, null); + writer.flush(); + } catch (Exception ex) { + throw new JsonException("Error writing object: " + object, ex); + } + if (INFO && logging) { + int end = buffer.position(); + buffer.position(start); + buffer.limit(end); + int length = end - start; + if (logBuffer.length < length) logBuffer = new byte[length]; + buffer.get(logBuffer, 0, length); + buffer.position(end); + buffer.limit(buffer.capacity()); + String message = new String(logBuffer, 0, length); + if (prettyPrint) message = json.prettyPrint(message); + info("Wrote: " + message); + } + } + + public Object read (Connection connection, ByteBuffer buffer) { + byteBufferInputStream.setByteBuffer(buffer); + return json.fromJson(Object.class, byteBufferInputStream); + } + + public void writeLength (ByteBuffer buffer, int length) { + buffer.putInt(length); + } + + public int readLength (ByteBuffer buffer) { + return buffer.getInt(); + } + + public int getLengthLength () { + return 4; + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/KryoNetException.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/KryoNetException.java similarity index 94% rename from kryonet/src/com/esotericsoftware/kryonet/KryoNetException.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/KryoNetException.java index a46305e7..f28ec2a5 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/KryoNetException.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/KryoNetException.java @@ -1,20 +1,20 @@ - -package com.esotericsoftware.kryonet; - -public class KryoNetException extends RuntimeException { - public KryoNetException () { - super(); - } - - public KryoNetException (String message, Throwable cause) { - super(message, cause); - } - - public KryoNetException (String message) { - super(message); - } - - public KryoNetException (Throwable cause) { - super(cause); - } -} + +package com.esotericsoftware.kryonet; + +public class KryoNetException extends RuntimeException { + public KryoNetException () { + super(); + } + + public KryoNetException (String message, Throwable cause) { + super(message, cause); + } + + public KryoNetException (String message) { + super(message); + } + + public KryoNetException (Throwable cause) { + super(cause); + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/KryoSerialization.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/KryoSerialization.java similarity index 96% rename from kryonet/src/com/esotericsoftware/kryonet/KryoSerialization.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/KryoSerialization.java index e8e54d45..d5b37b46 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/KryoSerialization.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/KryoSerialization.java @@ -1,72 +1,72 @@ - -package com.esotericsoftware.kryonet; - -import java.nio.ByteBuffer; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.io.ByteBufferInputStream; -import com.esotericsoftware.kryo.io.ByteBufferOutputStream; -import com.esotericsoftware.kryo.io.Input; -import com.esotericsoftware.kryo.io.Output; -import com.esotericsoftware.kryonet.FrameworkMessage.DiscoverHost; -import com.esotericsoftware.kryonet.FrameworkMessage.KeepAlive; -import com.esotericsoftware.kryonet.FrameworkMessage.Ping; -import com.esotericsoftware.kryonet.FrameworkMessage.RegisterTCP; -import com.esotericsoftware.kryonet.FrameworkMessage.RegisterUDP; - -public class KryoSerialization implements Serialization { - private final Kryo kryo; - private final Input input; - private final Output output; - private final ByteBufferInputStream byteBufferInputStream = new ByteBufferInputStream(); - private final ByteBufferOutputStream byteBufferOutputStream = new ByteBufferOutputStream(); - - public KryoSerialization () { - this(new Kryo()); - kryo.setReferences(false); - kryo.setRegistrationRequired(true); - } - - public KryoSerialization (Kryo kryo) { - this.kryo = kryo; - - kryo.register(RegisterTCP.class); - kryo.register(RegisterUDP.class); - kryo.register(KeepAlive.class); - kryo.register(DiscoverHost.class); - kryo.register(Ping.class); - - input = new Input(byteBufferInputStream, 512); - output = new Output(byteBufferOutputStream, 512); - } - - public Kryo getKryo () { - return kryo; - } - - public synchronized void write (Connection connection, ByteBuffer buffer, Object object) { - byteBufferOutputStream.setByteBuffer(buffer); - kryo.getContext().put("connection", connection); - kryo.writeClassAndObject(output, object); - output.flush(); - } - - public synchronized Object read (Connection connection, ByteBuffer buffer) { - byteBufferInputStream.setByteBuffer(buffer); - input.setInputStream(byteBufferInputStream); - kryo.getContext().put("connection", connection); - return kryo.readClassAndObject(input); - } - - public void writeLength (ByteBuffer buffer, int length) { - buffer.putInt(length); - } - - public int readLength (ByteBuffer buffer) { - return buffer.getInt(); - } - - public int getLengthLength () { - return 4; - } -} + +package com.esotericsoftware.kryonet; + +import java.nio.ByteBuffer; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.ByteBufferInputStream; +import com.esotericsoftware.kryo.io.ByteBufferOutputStream; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryonet.FrameworkMessage.DiscoverHost; +import com.esotericsoftware.kryonet.FrameworkMessage.KeepAlive; +import com.esotericsoftware.kryonet.FrameworkMessage.Ping; +import com.esotericsoftware.kryonet.FrameworkMessage.RegisterTCP; +import com.esotericsoftware.kryonet.FrameworkMessage.RegisterUDP; + +public class KryoSerialization implements Serialization { + private final Kryo kryo; + private final Input input; + private final Output output; + private final ByteBufferInputStream byteBufferInputStream = new ByteBufferInputStream(); + private final ByteBufferOutputStream byteBufferOutputStream = new ByteBufferOutputStream(); + + public KryoSerialization () { + this(new Kryo()); + kryo.setReferences(false); + kryo.setRegistrationRequired(true); + } + + public KryoSerialization (Kryo kryo) { + this.kryo = kryo; + + kryo.register(RegisterTCP.class); + kryo.register(RegisterUDP.class); + kryo.register(KeepAlive.class); + kryo.register(DiscoverHost.class); + kryo.register(Ping.class); + + input = new Input(byteBufferInputStream, 512); + output = new Output(byteBufferOutputStream, 512); + } + + public Kryo getKryo () { + return kryo; + } + + public synchronized void write (Connection connection, ByteBuffer buffer, Object object) { + byteBufferOutputStream.setByteBuffer(buffer); + kryo.getContext().put("connection", connection); + kryo.writeClassAndObject(output, object); + output.flush(); + } + + public synchronized Object read (Connection connection, ByteBuffer buffer) { + byteBufferInputStream.setByteBuffer(buffer); + input.setInputStream(byteBufferInputStream); + kryo.getContext().put("connection", connection); + return kryo.readClassAndObject(input); + } + + public void writeLength (ByteBuffer buffer, int length) { + buffer.putInt(length); + } + + public int readLength (ByteBuffer buffer) { + return buffer.getInt(); + } + + public int getLengthLength () { + return 4; + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/Listener.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/Listener.java similarity index 97% rename from kryonet/src/com/esotericsoftware/kryonet/Listener.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/Listener.java index 55aac6d3..fd14c6be 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/Listener.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/Listener.java @@ -1,170 +1,170 @@ - -package com.esotericsoftware.kryonet; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import static com.esotericsoftware.minlog.Log.*; - -/** Used to be notified about connection events. */ -public class Listener { - /** Called when the remote end has been connected. This will be invoked before any objects are received by - * {@link #received(Connection, Object)}. This will be invoked on the same thread as {@link Client#update(int)} and - * {@link Server#update(int)}. This method should not block for long periods as other network activity will not be processed - * until it returns. */ - public void connected (Connection connection) { - } - - /** Called when the remote end is no longer connected. There is no guarantee as to what thread will invoke this method. */ - public void disconnected (Connection connection) { - } - - /** Called when an object has been received from the remote end of the connection. This will be invoked on the same thread as - * {@link Client#update(int)} and {@link Server#update(int)}. This method should not block for long periods as other network - * activity will not be processed until it returns. */ - public void received (Connection connection, Object object) { - } - - /** Called when the connection is below the {@link Connection#setIdleThreshold(float) idle threshold}. */ - public void idle (Connection connection) { - } - - /** Uses reflection to called "received(Connection, XXX)" on the listener, where XXX is the received object type. Note this - * class uses a HashMap lookup and (cached) reflection, so is not as efficient as writing a series of "instanceof" statements. */ - static public class ReflectionListener extends Listener { - private final HashMap classToMethod = new HashMap(); - - public void received (Connection connection, Object object) { - Class type = object.getClass(); - Method method = classToMethod.get(type); - if (method == null) { - if (classToMethod.containsKey(type)) return; // Only fail on the first attempt to find the method. - try { - method = getClass().getMethod("received", new Class[] {Connection.class, type}); - } catch (SecurityException ex) { - if (ERROR) error("kryonet", "Unable to access method: received(Connection, " + type.getName() + ")", ex); - return; - } catch (NoSuchMethodException ex) { - if (DEBUG) - debug("kryonet", - "Unable to find listener method: " + getClass().getName() + "#received(Connection, " + type.getName() + ")"); - return; - } finally { - classToMethod.put(type, method); - } - } - try { - method.invoke(this, connection, object); - } catch (Throwable ex) { - if (ex instanceof InvocationTargetException && ex.getCause() != null) ex = ex.getCause(); - if (ex instanceof RuntimeException) throw (RuntimeException)ex; - throw new RuntimeException("Error invoking method: " + getClass().getName() + "#received(Connection, " - + type.getName() + ")", ex); - } - } - } - - /** Wraps a listener and queues notifications as {@link Runnable runnables}. This allows the runnables to be processed on a - * different thread, preventing the connection's update thread from being blocked. */ - static public abstract class QueuedListener extends Listener { - final Listener listener; - - public QueuedListener (Listener listener) { - if (listener == null) throw new IllegalArgumentException("listener cannot be null."); - this.listener = listener; - } - - public void connected (final Connection connection) { - queue(new Runnable() { - public void run () { - listener.connected(connection); - } - }); - } - - public void disconnected (final Connection connection) { - queue(new Runnable() { - public void run () { - listener.disconnected(connection); - } - }); - } - - public void received (final Connection connection, final Object object) { - queue(new Runnable() { - public void run () { - listener.received(connection, object); - } - }); - } - - public void idle (final Connection connection) { - queue(new Runnable() { - public void run () { - listener.idle(connection); - } - }); - } - - abstract protected void queue (Runnable runnable); - } - - /** Wraps a listener and processes notification events on a separate thread. */ - static public class ThreadedListener extends QueuedListener { - protected final ExecutorService threadPool; - - /** Creates a single thread to process notification events. */ - public ThreadedListener (Listener listener) { - this(listener, Executors.newFixedThreadPool(1)); - } - - /** Uses the specified threadPool to process notification events. */ - public ThreadedListener (Listener listener, ExecutorService threadPool) { - super(listener); - if (threadPool == null) throw new IllegalArgumentException("threadPool cannot be null."); - this.threadPool = threadPool; - } - - public void queue (Runnable runnable) { - threadPool.execute(runnable); - } - } - - /** Delays the notification of the wrapped listener to simulate lag on incoming objects. Notification events are processed on a - * separate thread after a delay. Note that only incoming objects are delayed. To delay outgoing objects, use a LagListener at - * the other end of the connection. */ - static public class LagListener extends QueuedListener { - private final ScheduledExecutorService threadPool; - private final int lagMillisMin, lagMillisMax; - final LinkedList runnables = new LinkedList(); - - public LagListener (int lagMillisMin, int lagMillisMax, Listener listener) { - super(listener); - this.lagMillisMin = lagMillisMin; - this.lagMillisMax = lagMillisMax; - threadPool = Executors.newScheduledThreadPool(1); - } - - public void queue (Runnable runnable) { - synchronized (runnables) { - runnables.addFirst(runnable); - } - int lag = lagMillisMin + (int)(Math.random() * (lagMillisMax - lagMillisMin)); - threadPool.schedule(new Runnable() { - public void run () { - Runnable runnable; - synchronized (runnables) { - runnable = runnables.removeLast(); - } - runnable.run(); - } - }, lag, TimeUnit.MILLISECONDS); - } - } -} + +package com.esotericsoftware.kryonet; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import static com.esotericsoftware.minlog.Log.*; + +/** Used to be notified about connection events. */ +public class Listener { + /** Called when the remote end has been connected. This will be invoked before any objects are received by + * {@link #received(Connection, Object)}. This will be invoked on the same thread as {@link Client#update(int)} and + * {@link Server#update(int)}. This method should not block for long periods as other network activity will not be processed + * until it returns. */ + public void connected (Connection connection) { + } + + /** Called when the remote end is no longer connected. There is no guarantee as to what thread will invoke this method. */ + public void disconnected (Connection connection) { + } + + /** Called when an object has been received from the remote end of the connection. This will be invoked on the same thread as + * {@link Client#update(int)} and {@link Server#update(int)}. This method should not block for long periods as other network + * activity will not be processed until it returns. */ + public void received (Connection connection, Object object) { + } + + /** Called when the connection is below the {@link Connection#setIdleThreshold(float) idle threshold}. */ + public void idle (Connection connection) { + } + + /** Uses reflection to called "received(Connection, XXX)" on the listener, where XXX is the received object type. Note this + * class uses a HashMap lookup and (cached) reflection, so is not as efficient as writing a series of "instanceof" statements. */ + static public class ReflectionListener extends Listener { + private final HashMap classToMethod = new HashMap(); + + public void received (Connection connection, Object object) { + Class type = object.getClass(); + Method method = classToMethod.get(type); + if (method == null) { + if (classToMethod.containsKey(type)) return; // Only fail on the first attempt to find the method. + try { + method = getClass().getMethod("received", new Class[] {Connection.class, type}); + } catch (SecurityException ex) { + if (ERROR) error("kryonet", "Unable to access method: received(Connection, " + type.getName() + ")", ex); + return; + } catch (NoSuchMethodException ex) { + if (DEBUG) + debug("kryonet", + "Unable to find listener method: " + getClass().getName() + "#received(Connection, " + type.getName() + ")"); + return; + } finally { + classToMethod.put(type, method); + } + } + try { + method.invoke(this, connection, object); + } catch (Throwable ex) { + if (ex instanceof InvocationTargetException && ex.getCause() != null) ex = ex.getCause(); + if (ex instanceof RuntimeException) throw (RuntimeException)ex; + throw new RuntimeException("Error invoking method: " + getClass().getName() + "#received(Connection, " + + type.getName() + ")", ex); + } + } + } + + /** Wraps a listener and queues notifications as {@link Runnable runnables}. This allows the runnables to be processed on a + * different thread, preventing the connection's update thread from being blocked. */ + static public abstract class QueuedListener extends Listener { + final Listener listener; + + public QueuedListener (Listener listener) { + if (listener == null) throw new IllegalArgumentException("listener cannot be null."); + this.listener = listener; + } + + public void connected (final Connection connection) { + queue(new Runnable() { + public void run () { + listener.connected(connection); + } + }); + } + + public void disconnected (final Connection connection) { + queue(new Runnable() { + public void run () { + listener.disconnected(connection); + } + }); + } + + public void received (final Connection connection, final Object object) { + queue(new Runnable() { + public void run () { + listener.received(connection, object); + } + }); + } + + public void idle (final Connection connection) { + queue(new Runnable() { + public void run () { + listener.idle(connection); + } + }); + } + + abstract protected void queue (Runnable runnable); + } + + /** Wraps a listener and processes notification events on a separate thread. */ + static public class ThreadedListener extends QueuedListener { + protected final ExecutorService threadPool; + + /** Creates a single thread to process notification events. */ + public ThreadedListener (Listener listener) { + this(listener, Executors.newFixedThreadPool(1)); + } + + /** Uses the specified threadPool to process notification events. */ + public ThreadedListener (Listener listener, ExecutorService threadPool) { + super(listener); + if (threadPool == null) throw new IllegalArgumentException("threadPool cannot be null."); + this.threadPool = threadPool; + } + + public void queue (Runnable runnable) { + threadPool.execute(runnable); + } + } + + /** Delays the notification of the wrapped listener to simulate lag on incoming objects. Notification events are processed on a + * separate thread after a delay. Note that only incoming objects are delayed. To delay outgoing objects, use a LagListener at + * the other end of the connection. */ + static public class LagListener extends QueuedListener { + private final ScheduledExecutorService threadPool; + private final int lagMillisMin, lagMillisMax; + final LinkedList runnables = new LinkedList(); + + public LagListener (int lagMillisMin, int lagMillisMax, Listener listener) { + super(listener); + this.lagMillisMin = lagMillisMin; + this.lagMillisMax = lagMillisMax; + threadPool = Executors.newScheduledThreadPool(1); + } + + public void queue (Runnable runnable) { + synchronized (runnables) { + runnables.addFirst(runnable); + } + int lag = lagMillisMin + (int)(Math.random() * (lagMillisMax - lagMillisMin)); + threadPool.schedule(new Runnable() { + public void run () { + Runnable runnable; + synchronized (runnables) { + runnable = runnables.removeLast(); + } + runnable.run(); + } + }, lag, TimeUnit.MILLISECONDS); + } + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/Serialization.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/Serialization.java similarity index 96% rename from kryonet/src/com/esotericsoftware/kryonet/Serialization.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/Serialization.java index 58002b69..1d9e3a3c 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/Serialization.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/Serialization.java @@ -1,20 +1,20 @@ - -package com.esotericsoftware.kryonet; - -import java.nio.ByteBuffer; - -/** Controls how objects are transmitted over the network. */ -public interface Serialization { - /** @param connection May be null. */ - public void write (Connection connection, ByteBuffer buffer, Object object); - - public Object read (Connection connection, ByteBuffer buffer); - - /** The fixed number of bytes that will be written by {@link #writeLength(ByteBuffer, int)} and read by - * {@link #readLength(ByteBuffer)}. */ - public int getLengthLength (); - - public void writeLength (ByteBuffer buffer, int length); - - public int readLength (ByteBuffer buffer); -} + +package com.esotericsoftware.kryonet; + +import java.nio.ByteBuffer; + +/** Controls how objects are transmitted over the network. */ +public interface Serialization { + /** @param connection May be null. */ + public void write (Connection connection, ByteBuffer buffer, Object object); + + public Object read (Connection connection, ByteBuffer buffer); + + /** The fixed number of bytes that will be written by {@link #writeLength(ByteBuffer, int)} and read by + * {@link #readLength(ByteBuffer)}. */ + public int getLengthLength (); + + public void writeLength (ByteBuffer buffer, int length); + + public int readLength (ByteBuffer buffer); +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/Server.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/Server.java similarity index 97% rename from kryonet/src/com/esotericsoftware/kryonet/Server.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/Server.java index a8354934..2c4b2833 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/Server.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/Server.java @@ -1,560 +1,560 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.CancelledKeyException; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.Set; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.util.IntMap; -import com.esotericsoftware.kryonet.FrameworkMessage.DiscoverHost; -import com.esotericsoftware.kryonet.FrameworkMessage.RegisterTCP; -import com.esotericsoftware.kryonet.FrameworkMessage.RegisterUDP; - -import static com.esotericsoftware.minlog.Log.*; - -/** Manages TCP and optionally UDP connections from many {@link Client Clients}. - * @author Nathan Sweet */ -public class Server implements EndPoint { - private final Serialization serialization; - private final int writeBufferSize, objectBufferSize; - private final Selector selector; - private int emptySelects; - private ServerSocketChannel serverChannel; - private UdpConnection udp; - private Connection[] connections = {}; - private IntMap pendingConnections = new IntMap(); - Listener[] listeners = {}; - private Object listenerLock = new Object(); - private int nextConnectionID = 1; - private volatile boolean shutdown; - private Object updateLock = new Object(); - private Thread updateThread; - private ByteBuffer emptyBuffer = ByteBuffer.allocate(0); - - private Listener dispatchListener = new Listener() { - public void connected (Connection connection) { - Listener[] listeners = Server.this.listeners; - for (int i = 0, n = listeners.length; i < n; i++) - listeners[i].connected(connection); - } - - public void disconnected (Connection connection) { - removeConnection(connection); - Listener[] listeners = Server.this.listeners; - for (int i = 0, n = listeners.length; i < n; i++) - listeners[i].disconnected(connection); - } - - public void received (Connection connection, Object object) { - Listener[] listeners = Server.this.listeners; - for (int i = 0, n = listeners.length; i < n; i++) - listeners[i].received(connection, object); - } - - public void idle (Connection connection) { - Listener[] listeners = Server.this.listeners; - for (int i = 0, n = listeners.length; i < n; i++) - listeners[i].idle(connection); - } - }; - - /** Creates a Server with a write buffer size of 16384 and an object buffer size of 2048. */ - public Server () { - this(16384, 2048); - } - - /** @param writeBufferSize One buffer of this size is allocated for each connected client. Objects are serialized to the write - * buffer where the bytes are queued until they can be written to the TCP socket. - *

    - * Normally the socket is writable and the bytes are written immediately. If the socket cannot be written to and - * enough serialized objects are queued to overflow the buffer, then the connection will be closed. - *

    - * The write buffer should be sized at least as large as the largest object that will be sent, plus some head room to - * allow for some serialized objects to be queued in case the buffer is temporarily not writable. The amount of head - * room needed is dependent upon the size of objects being sent and how often they are sent. - * @param objectBufferSize One (using only TCP) or three (using both TCP and UDP) buffers of this size are allocated. These - * buffers are used to hold the bytes for a single object graph until it can be sent over the network or - * deserialized. - *

    - * The object buffers should be sized at least as large as the largest object that will be sent or received. */ - public Server (int writeBufferSize, int objectBufferSize) { - this(writeBufferSize, objectBufferSize, new KryoSerialization()); - } - - public Server (int writeBufferSize, int objectBufferSize, Serialization serialization) { - this.writeBufferSize = writeBufferSize; - this.objectBufferSize = objectBufferSize; - - this.serialization = serialization; - - try { - selector = Selector.open(); - } catch (IOException ex) { - throw new RuntimeException("Error opening selector.", ex); - } - } - - public Serialization getSerialization () { - return serialization; - } - - public Kryo getKryo () { - return ((KryoSerialization)serialization).getKryo(); - } - - /** Opens a TCP only server. - * @throws IOException if the server could not be opened. */ - public void bind (int tcpPort) throws IOException { - bind(new InetSocketAddress(tcpPort), null); - } - - /** Opens a TCP and UDP server. - * @throws IOException if the server could not be opened. */ - public void bind (int tcpPort, int udpPort) throws IOException { - bind(new InetSocketAddress(tcpPort), new InetSocketAddress(udpPort)); - } - - /** @param udpPort May be null. */ - public void bind (InetSocketAddress tcpPort, InetSocketAddress udpPort) throws IOException { - close(); - synchronized (updateLock) { - selector.wakeup(); - try { - serverChannel = selector.provider().openServerSocketChannel(); - serverChannel.socket().bind(tcpPort); - serverChannel.configureBlocking(false); - serverChannel.register(selector, SelectionKey.OP_ACCEPT); - if (DEBUG) debug("kryonet", "Accepting connections on port: " + tcpPort + "/TCP"); - - if (udpPort != null) { - udp = new UdpConnection(serialization, objectBufferSize); - udp.bind(selector, udpPort); - if (DEBUG) debug("kryonet", "Accepting connections on port: " + udpPort + "/UDP"); - } - } catch (IOException ex) { - close(); - throw ex; - } - } - if (INFO) info("kryonet", "Server opened."); - } - - /** Accepts any new connections and reads or writes any pending data for the current connections. - * @param timeout Wait for up to the specified milliseconds for a connection to be ready to process. May be zero to return - * immediately if there are no connections to process. */ - public void update (int timeout) throws IOException { - updateThread = Thread.currentThread(); - synchronized (updateLock) { // Blocks to avoid a select while the selector is used to bind the server connection. - } - long startTime = System.currentTimeMillis(); - int select = 0; - if (timeout > 0) { - select = selector.select(timeout); - } else { - select = selector.selectNow(); - } - if (select == 0) { - emptySelects++; - if (emptySelects == 100) { - emptySelects = 0; - // NIO freaks and returns immediately with 0 sometimes, so try to keep from hogging the CPU. - long elapsedTime = System.currentTimeMillis() - startTime; - try { - if (elapsedTime < 25) Thread.sleep(25 - elapsedTime); - } catch (InterruptedException ex) { - } - } - } else { - emptySelects = 0; - Set keys = selector.selectedKeys(); - synchronized (keys) { - UdpConnection udp = this.udp; - outer: - for (Iterator iter = keys.iterator(); iter.hasNext();) { - SelectionKey selectionKey = iter.next(); - iter.remove(); - Connection fromConnection = (Connection)selectionKey.attachment(); - try { - int ops = selectionKey.readyOps(); - - if (fromConnection != null) { // Must be a TCP read or write operation. - if (udp != null && fromConnection.udpRemoteAddress == null) { - fromConnection.close(); - continue; - } - if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ) { - try { - while (true) { - Object object = fromConnection.tcp.readObject(fromConnection); - if (object == null) break; - if (DEBUG) { - String objectString = object == null ? "null" : object.getClass().getSimpleName(); - if (!(object instanceof FrameworkMessage)) { - debug("kryonet", fromConnection + " received TCP: " + objectString); - } else if (TRACE) { - trace("kryonet", fromConnection + " received TCP: " + objectString); - } - } - fromConnection.notifyReceived(object); - } - } catch (IOException ex) { - if (TRACE) { - trace("kryonet", "Unable to read TCP from: " + fromConnection, ex); - } else if (DEBUG) { - debug("kryonet", fromConnection + " update: " + ex.getMessage()); - } - fromConnection.close(); - } catch (KryoNetException ex) { - if (ERROR) error("kryonet", "Error reading TCP from connection: " + fromConnection, ex); - fromConnection.close(); - } - } - if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) { - try { - fromConnection.tcp.writeOperation(); - } catch (IOException ex) { - if (TRACE) { - trace("kryonet", "Unable to write TCP to connection: " + fromConnection, ex); - } else if (DEBUG) { - debug("kryonet", fromConnection + " update: " + ex.getMessage()); - } - fromConnection.close(); - } - } - continue; - } - - if ((ops & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) { - ServerSocketChannel serverChannel = this.serverChannel; - if (serverChannel == null) continue; - try { - SocketChannel socketChannel = serverChannel.accept(); - if (socketChannel != null) acceptOperation(socketChannel); - } catch (IOException ex) { - if (DEBUG) debug("kryonet", "Unable to accept new connection.", ex); - } - continue; - } - - // Must be a UDP read operation. - if (udp == null) { - selectionKey.channel().close(); - continue; - } - InetSocketAddress fromAddress; - try { - fromAddress = udp.readFromAddress(); - } catch (IOException ex) { - if (WARN) warn("kryonet", "Error reading UDP data.", ex); - continue; - } - if (fromAddress == null) continue; - - Connection[] connections = this.connections; - for (int i = 0, n = connections.length; i < n; i++) { - Connection connection = connections[i]; - if (fromAddress.equals(connection.udpRemoteAddress)) { - fromConnection = connection; - break; - } - } - - Object object; - try { - object = udp.readObject(fromConnection); - } catch (KryoNetException ex) { - if (WARN) { - if (fromConnection != null) { - if (ERROR) error("kryonet", "Error reading UDP from connection: " + fromConnection, ex); - } else - warn("kryonet", "Error reading UDP from unregistered address: " + fromAddress, ex); - } - continue; - } - - if (object instanceof FrameworkMessage) { - if (object instanceof RegisterUDP) { - // Store the fromAddress on the connection and reply over TCP with a RegisterUDP to indicate success. - int fromConnectionID = ((RegisterUDP)object).connectionID; - Connection connection = pendingConnections.remove(fromConnectionID); - if (connection != null) { - if (connection.udpRemoteAddress != null) continue outer; - connection.udpRemoteAddress = fromAddress; - addConnection(connection); - connection.sendTCP(new RegisterUDP()); - if (DEBUG) - debug("kryonet", "Port " + udp.datagramChannel.socket().getLocalPort() + "/UDP connected to: " - + fromAddress); - connection.notifyConnected(); - continue; - } - if (DEBUG) - debug("kryonet", "Ignoring incoming RegisterUDP with invalid connection ID: " + fromConnectionID); - continue; - } - if (object instanceof DiscoverHost) { - try { - udp.datagramChannel.send(emptyBuffer, fromAddress); - if (DEBUG) debug("kryonet", "Responded to host discovery from: " + fromAddress); - } catch (IOException ex) { - if (WARN) warn("kryonet", "Error replying to host discovery from: " + fromAddress, ex); - } - continue; - } - } - - if (fromConnection != null) { - if (DEBUG) { - String objectString = object == null ? "null" : object.getClass().getSimpleName(); - if (object instanceof FrameworkMessage) { - if (TRACE) trace("kryonet", fromConnection + " received UDP: " + objectString); - } else - debug("kryonet", fromConnection + " received UDP: " + objectString); - } - fromConnection.notifyReceived(object); - continue; - } - if (DEBUG) debug("kryonet", "Ignoring UDP from unregistered address: " + fromAddress); - } catch (CancelledKeyException ex) { - if (fromConnection != null) - fromConnection.close(); - else - selectionKey.channel().close(); - } - } - } - } - long time = System.currentTimeMillis(); - Connection[] connections = this.connections; - for (int i = 0, n = connections.length; i < n; i++) { - Connection connection = connections[i]; - if (connection.tcp.isTimedOut(time)) { - if (DEBUG) debug("kryonet", connection + " timed out."); - connection.close(); - } else { - if (connection.tcp.needsKeepAlive(time)) connection.sendTCP(FrameworkMessage.keepAlive); - } - if (connection.isIdle()) connection.notifyIdle(); - } - } - - public void run () { - if (TRACE) trace("kryonet", "Server thread started."); - shutdown = false; - while (!shutdown) { - try { - update(250); - } catch (IOException ex) { - if (ERROR) error("kryonet", "Error updating server connections.", ex); - close(); - } - } - if (TRACE) trace("kryonet", "Server thread stopped."); - } - - public void start () { - new Thread(this, "Server").start(); - } - - public void stop () { - if (shutdown) return; - close(); - if (TRACE) trace("kryonet", "Server thread stopping."); - shutdown = true; - } - - private void acceptOperation (SocketChannel socketChannel) { - Connection connection = newConnection(); - connection.initialize(serialization, writeBufferSize, objectBufferSize); - connection.endPoint = this; - UdpConnection udp = this.udp; - if (udp != null) connection.udp = udp; - try { - SelectionKey selectionKey = connection.tcp.accept(selector, socketChannel); - selectionKey.attach(connection); - - int id = nextConnectionID++; - if (nextConnectionID == -1) nextConnectionID = 1; - connection.id = id; - connection.setConnected(true); - connection.addListener(dispatchListener); - - if (udp == null) - addConnection(connection); - else - pendingConnections.put(id, connection); - - RegisterTCP registerConnection = new RegisterTCP(); - registerConnection.connectionID = id; - connection.sendTCP(registerConnection); - - if (udp == null) connection.notifyConnected(); - } catch (IOException ex) { - connection.close(); - if (DEBUG) debug("kryonet", "Unable to accept TCP connection.", ex); - } - } - - /** Allows the connections used by the server to be subclassed. This can be useful for storage per connection without an - * additional lookup. */ - protected Connection newConnection () { - return new Connection(); - } - - private void addConnection (Connection connection) { - Connection[] newConnections = new Connection[connections.length + 1]; - newConnections[0] = connection; - System.arraycopy(connections, 0, newConnections, 1, connections.length); - connections = newConnections; - } - - void removeConnection (Connection connection) { - ArrayList temp = new ArrayList(Arrays.asList(connections)); - temp.remove(connection); - connections = temp.toArray(new Connection[temp.size()]); - - pendingConnections.remove(connection.id); - } - - // BOZO - Provide mechanism for sending to multiple clients without serializing multiple times. - - public void sendToAllTCP (Object object) { - Connection[] connections = this.connections; - for (int i = 0, n = connections.length; i < n; i++) { - Connection connection = connections[i]; - connection.sendTCP(object); - } - } - - public void sendToAllExceptTCP (int connectionID, Object object) { - Connection[] connections = this.connections; - for (int i = 0, n = connections.length; i < n; i++) { - Connection connection = connections[i]; - if (connection.id != connectionID) connection.sendTCP(object); - } - } - - public void sendToTCP (int connectionID, Object object) { - Connection[] connections = this.connections; - for (int i = 0, n = connections.length; i < n; i++) { - Connection connection = connections[i]; - if (connection.id == connectionID) { - connection.sendTCP(object); - break; - } - } - } - - public void sendToAllUDP (Object object) { - Connection[] connections = this.connections; - for (int i = 0, n = connections.length; i < n; i++) { - Connection connection = connections[i]; - connection.sendUDP(object); - } - } - - public void sendToAllExceptUDP (int connectionID, Object object) { - Connection[] connections = this.connections; - for (int i = 0, n = connections.length; i < n; i++) { - Connection connection = connections[i]; - if (connection.id != connectionID) connection.sendUDP(object); - } - } - - public void sendToUDP (int connectionID, Object object) { - Connection[] connections = this.connections; - for (int i = 0, n = connections.length; i < n; i++) { - Connection connection = connections[i]; - if (connection.id == connectionID) { - connection.sendUDP(object); - break; - } - } - } - - public void addListener (Listener listener) { - if (listener == null) throw new IllegalArgumentException("listener cannot be null."); - synchronized (listenerLock) { - Listener[] listeners = this.listeners; - int n = listeners.length; - for (int i = 0; i < n; i++) - if (listener == listeners[i]) return; - Listener[] newListeners = new Listener[n + 1]; - newListeners[0] = listener; - System.arraycopy(listeners, 0, newListeners, 1, n); - this.listeners = newListeners; - } - if (TRACE) trace("kryonet", "Server listener added: " + listener.getClass().getName()); - } - - public void removeListener (Listener listener) { - if (listener == null) throw new IllegalArgumentException("listener cannot be null."); - synchronized (listenerLock) { - Listener[] listeners = this.listeners; - int n = listeners.length; - Listener[] newListeners = new Listener[n - 1]; - for (int i = 0, ii = 0; i < n; i++) { - Listener copyListener = listeners[i]; - if (listener == copyListener) continue; - if (ii == n - 1) return; - newListeners[ii++] = copyListener; - } - this.listeners = newListeners; - } - if (TRACE) trace("kryonet", "Server listener removed: " + listener.getClass().getName()); - } - - /** Closes all open connections and the server port(s). */ - public void close () { - Connection[] connections = this.connections; - if (INFO && connections.length > 0) info("kryonet", "Closing server connections..."); - for (int i = 0, n = connections.length; i < n; i++) - connections[i].close(); - connections = new Connection[0]; - - ServerSocketChannel serverChannel = this.serverChannel; - if (serverChannel != null) { - try { - serverChannel.close(); - if (INFO) info("kryonet", "Server closed."); - } catch (IOException ex) { - if (DEBUG) debug("kryonet", "Unable to close server.", ex); - } - this.serverChannel = null; - } - - UdpConnection udp = this.udp; - if (udp != null) { - udp.close(); - this.udp = null; - } - - // Select one last time to complete closing the socket. - synchronized (updateLock) { - selector.wakeup(); - try { - selector.selectNow(); - } catch (IOException ignored) { - } - } - } - - public Thread getUpdateThread () { - return updateThread; - } - - /** Returns the current connections. The array returned should not be modified. */ - public Connection[] getConnections () { - return connections; - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.CancelledKeyException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Set; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.util.IntMap; +import com.esotericsoftware.kryonet.FrameworkMessage.DiscoverHost; +import com.esotericsoftware.kryonet.FrameworkMessage.RegisterTCP; +import com.esotericsoftware.kryonet.FrameworkMessage.RegisterUDP; + +import static com.esotericsoftware.minlog.Log.*; + +/** Manages TCP and optionally UDP connections from many {@link Client Clients}. + * @author Nathan Sweet */ +public class Server implements EndPoint { + private final Serialization serialization; + private final int writeBufferSize, objectBufferSize; + private final Selector selector; + private int emptySelects; + private ServerSocketChannel serverChannel; + private UdpConnection udp; + private Connection[] connections = {}; + private IntMap pendingConnections = new IntMap(); + Listener[] listeners = {}; + private Object listenerLock = new Object(); + private int nextConnectionID = 1; + private volatile boolean shutdown; + private Object updateLock = new Object(); + private Thread updateThread; + private ByteBuffer emptyBuffer = ByteBuffer.allocate(0); + + private Listener dispatchListener = new Listener() { + public void connected (Connection connection) { + Listener[] listeners = Server.this.listeners; + for (int i = 0, n = listeners.length; i < n; i++) + listeners[i].connected(connection); + } + + public void disconnected (Connection connection) { + removeConnection(connection); + Listener[] listeners = Server.this.listeners; + for (int i = 0, n = listeners.length; i < n; i++) + listeners[i].disconnected(connection); + } + + public void received (Connection connection, Object object) { + Listener[] listeners = Server.this.listeners; + for (int i = 0, n = listeners.length; i < n; i++) + listeners[i].received(connection, object); + } + + public void idle (Connection connection) { + Listener[] listeners = Server.this.listeners; + for (int i = 0, n = listeners.length; i < n; i++) + listeners[i].idle(connection); + } + }; + + /** Creates a Server with a write buffer size of 16384 and an object buffer size of 2048. */ + public Server () { + this(16384, 2048); + } + + /** @param writeBufferSize One buffer of this size is allocated for each connected client. Objects are serialized to the write + * buffer where the bytes are queued until they can be written to the TCP socket. + *

    + * Normally the socket is writable and the bytes are written immediately. If the socket cannot be written to and + * enough serialized objects are queued to overflow the buffer, then the connection will be closed. + *

    + * The write buffer should be sized at least as large as the largest object that will be sent, plus some head room to + * allow for some serialized objects to be queued in case the buffer is temporarily not writable. The amount of head + * room needed is dependent upon the size of objects being sent and how often they are sent. + * @param objectBufferSize One (using only TCP) or three (using both TCP and UDP) buffers of this size are allocated. These + * buffers are used to hold the bytes for a single object graph until it can be sent over the network or + * deserialized. + *

    + * The object buffers should be sized at least as large as the largest object that will be sent or received. */ + public Server (int writeBufferSize, int objectBufferSize) { + this(writeBufferSize, objectBufferSize, new KryoSerialization()); + } + + public Server (int writeBufferSize, int objectBufferSize, Serialization serialization) { + this.writeBufferSize = writeBufferSize; + this.objectBufferSize = objectBufferSize; + + this.serialization = serialization; + + try { + selector = Selector.open(); + } catch (IOException ex) { + throw new RuntimeException("Error opening selector.", ex); + } + } + + public Serialization getSerialization () { + return serialization; + } + + public Kryo getKryo () { + return ((KryoSerialization)serialization).getKryo(); + } + + /** Opens a TCP only server. + * @throws IOException if the server could not be opened. */ + public void bind (int tcpPort) throws IOException { + bind(new InetSocketAddress(tcpPort), null); + } + + /** Opens a TCP and UDP server. + * @throws IOException if the server could not be opened. */ + public void bind (int tcpPort, int udpPort) throws IOException { + bind(new InetSocketAddress(tcpPort), new InetSocketAddress(udpPort)); + } + + /** @param udpPort May be null. */ + public void bind (InetSocketAddress tcpPort, InetSocketAddress udpPort) throws IOException { + close(); + synchronized (updateLock) { + selector.wakeup(); + try { + serverChannel = selector.provider().openServerSocketChannel(); + serverChannel.socket().bind(tcpPort); + serverChannel.configureBlocking(false); + serverChannel.register(selector, SelectionKey.OP_ACCEPT); + if (DEBUG) debug("kryonet", "Accepting connections on port: " + tcpPort + "/TCP"); + + if (udpPort != null) { + udp = new UdpConnection(serialization, objectBufferSize); + udp.bind(selector, udpPort); + if (DEBUG) debug("kryonet", "Accepting connections on port: " + udpPort + "/UDP"); + } + } catch (IOException ex) { + close(); + throw ex; + } + } + if (INFO) info("kryonet", "Server opened."); + } + + /** Accepts any new connections and reads or writes any pending data for the current connections. + * @param timeout Wait for up to the specified milliseconds for a connection to be ready to process. May be zero to return + * immediately if there are no connections to process. */ + public void update (int timeout) throws IOException { + updateThread = Thread.currentThread(); + synchronized (updateLock) { // Blocks to avoid a select while the selector is used to bind the server connection. + } + long startTime = System.currentTimeMillis(); + int select = 0; + if (timeout > 0) { + select = selector.select(timeout); + } else { + select = selector.selectNow(); + } + if (select == 0) { + emptySelects++; + if (emptySelects == 100) { + emptySelects = 0; + // NIO freaks and returns immediately with 0 sometimes, so try to keep from hogging the CPU. + long elapsedTime = System.currentTimeMillis() - startTime; + try { + if (elapsedTime < 25) Thread.sleep(25 - elapsedTime); + } catch (InterruptedException ex) { + } + } + } else { + emptySelects = 0; + Set keys = selector.selectedKeys(); + synchronized (keys) { + UdpConnection udp = this.udp; + outer: + for (Iterator iter = keys.iterator(); iter.hasNext();) { + SelectionKey selectionKey = iter.next(); + iter.remove(); + Connection fromConnection = (Connection)selectionKey.attachment(); + try { + int ops = selectionKey.readyOps(); + + if (fromConnection != null) { // Must be a TCP read or write operation. + if (udp != null && fromConnection.udpRemoteAddress == null) { + fromConnection.close(); + continue; + } + if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ) { + try { + while (true) { + Object object = fromConnection.tcp.readObject(fromConnection); + if (object == null) break; + if (DEBUG) { + String objectString = object == null ? "null" : object.getClass().getSimpleName(); + if (!(object instanceof FrameworkMessage)) { + debug("kryonet", fromConnection + " received TCP: " + objectString); + } else if (TRACE) { + trace("kryonet", fromConnection + " received TCP: " + objectString); + } + } + fromConnection.notifyReceived(object); + } + } catch (IOException ex) { + if (TRACE) { + trace("kryonet", "Unable to read TCP from: " + fromConnection, ex); + } else if (DEBUG) { + debug("kryonet", fromConnection + " update: " + ex.getMessage()); + } + fromConnection.close(); + } catch (KryoNetException ex) { + if (ERROR) error("kryonet", "Error reading TCP from connection: " + fromConnection, ex); + fromConnection.close(); + } + } + if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) { + try { + fromConnection.tcp.writeOperation(); + } catch (IOException ex) { + if (TRACE) { + trace("kryonet", "Unable to write TCP to connection: " + fromConnection, ex); + } else if (DEBUG) { + debug("kryonet", fromConnection + " update: " + ex.getMessage()); + } + fromConnection.close(); + } + } + continue; + } + + if ((ops & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) { + ServerSocketChannel serverChannel = this.serverChannel; + if (serverChannel == null) continue; + try { + SocketChannel socketChannel = serverChannel.accept(); + if (socketChannel != null) acceptOperation(socketChannel); + } catch (IOException ex) { + if (DEBUG) debug("kryonet", "Unable to accept new connection.", ex); + } + continue; + } + + // Must be a UDP read operation. + if (udp == null) { + selectionKey.channel().close(); + continue; + } + InetSocketAddress fromAddress; + try { + fromAddress = udp.readFromAddress(); + } catch (IOException ex) { + if (WARN) warn("kryonet", "Error reading UDP data.", ex); + continue; + } + if (fromAddress == null) continue; + + Connection[] connections = this.connections; + for (int i = 0, n = connections.length; i < n; i++) { + Connection connection = connections[i]; + if (fromAddress.equals(connection.udpRemoteAddress)) { + fromConnection = connection; + break; + } + } + + Object object; + try { + object = udp.readObject(fromConnection); + } catch (KryoNetException ex) { + if (WARN) { + if (fromConnection != null) { + if (ERROR) error("kryonet", "Error reading UDP from connection: " + fromConnection, ex); + } else + warn("kryonet", "Error reading UDP from unregistered address: " + fromAddress, ex); + } + continue; + } + + if (object instanceof FrameworkMessage) { + if (object instanceof RegisterUDP) { + // Store the fromAddress on the connection and reply over TCP with a RegisterUDP to indicate success. + int fromConnectionID = ((RegisterUDP)object).connectionID; + Connection connection = pendingConnections.remove(fromConnectionID); + if (connection != null) { + if (connection.udpRemoteAddress != null) continue outer; + connection.udpRemoteAddress = fromAddress; + addConnection(connection); + connection.sendTCP(new RegisterUDP()); + if (DEBUG) + debug("kryonet", "Port " + udp.datagramChannel.socket().getLocalPort() + "/UDP connected to: " + + fromAddress); + connection.notifyConnected(); + continue; + } + if (DEBUG) + debug("kryonet", "Ignoring incoming RegisterUDP with invalid connection ID: " + fromConnectionID); + continue; + } + if (object instanceof DiscoverHost) { + try { + udp.datagramChannel.send(emptyBuffer, fromAddress); + if (DEBUG) debug("kryonet", "Responded to host discovery from: " + fromAddress); + } catch (IOException ex) { + if (WARN) warn("kryonet", "Error replying to host discovery from: " + fromAddress, ex); + } + continue; + } + } + + if (fromConnection != null) { + if (DEBUG) { + String objectString = object == null ? "null" : object.getClass().getSimpleName(); + if (object instanceof FrameworkMessage) { + if (TRACE) trace("kryonet", fromConnection + " received UDP: " + objectString); + } else + debug("kryonet", fromConnection + " received UDP: " + objectString); + } + fromConnection.notifyReceived(object); + continue; + } + if (DEBUG) debug("kryonet", "Ignoring UDP from unregistered address: " + fromAddress); + } catch (CancelledKeyException ex) { + if (fromConnection != null) + fromConnection.close(); + else + selectionKey.channel().close(); + } + } + } + } + long time = System.currentTimeMillis(); + Connection[] connections = this.connections; + for (int i = 0, n = connections.length; i < n; i++) { + Connection connection = connections[i]; + if (connection.tcp.isTimedOut(time)) { + if (DEBUG) debug("kryonet", connection + " timed out."); + connection.close(); + } else { + if (connection.tcp.needsKeepAlive(time)) connection.sendTCP(FrameworkMessage.keepAlive); + } + if (connection.isIdle()) connection.notifyIdle(); + } + } + + public void run () { + if (TRACE) trace("kryonet", "Server thread started."); + shutdown = false; + while (!shutdown) { + try { + update(250); + } catch (IOException ex) { + if (ERROR) error("kryonet", "Error updating server connections.", ex); + close(); + } + } + if (TRACE) trace("kryonet", "Server thread stopped."); + } + + public void start () { + new Thread(this, "Server").start(); + } + + public void stop () { + if (shutdown) return; + close(); + if (TRACE) trace("kryonet", "Server thread stopping."); + shutdown = true; + } + + private void acceptOperation (SocketChannel socketChannel) { + Connection connection = newConnection(); + connection.initialize(serialization, writeBufferSize, objectBufferSize); + connection.endPoint = this; + UdpConnection udp = this.udp; + if (udp != null) connection.udp = udp; + try { + SelectionKey selectionKey = connection.tcp.accept(selector, socketChannel); + selectionKey.attach(connection); + + int id = nextConnectionID++; + if (nextConnectionID == -1) nextConnectionID = 1; + connection.id = id; + connection.setConnected(true); + connection.addListener(dispatchListener); + + if (udp == null) + addConnection(connection); + else + pendingConnections.put(id, connection); + + RegisterTCP registerConnection = new RegisterTCP(); + registerConnection.connectionID = id; + connection.sendTCP(registerConnection); + + if (udp == null) connection.notifyConnected(); + } catch (IOException ex) { + connection.close(); + if (DEBUG) debug("kryonet", "Unable to accept TCP connection.", ex); + } + } + + /** Allows the connections used by the server to be subclassed. This can be useful for storage per connection without an + * additional lookup. */ + protected Connection newConnection () { + return new Connection(); + } + + private void addConnection (Connection connection) { + Connection[] newConnections = new Connection[connections.length + 1]; + newConnections[0] = connection; + System.arraycopy(connections, 0, newConnections, 1, connections.length); + connections = newConnections; + } + + void removeConnection (Connection connection) { + ArrayList temp = new ArrayList(Arrays.asList(connections)); + temp.remove(connection); + connections = temp.toArray(new Connection[temp.size()]); + + pendingConnections.remove(connection.id); + } + + // BOZO - Provide mechanism for sending to multiple clients without serializing multiple times. + + public void sendToAllTCP (Object object) { + Connection[] connections = this.connections; + for (int i = 0, n = connections.length; i < n; i++) { + Connection connection = connections[i]; + connection.sendTCP(object); + } + } + + public void sendToAllExceptTCP (int connectionID, Object object) { + Connection[] connections = this.connections; + for (int i = 0, n = connections.length; i < n; i++) { + Connection connection = connections[i]; + if (connection.id != connectionID) connection.sendTCP(object); + } + } + + public void sendToTCP (int connectionID, Object object) { + Connection[] connections = this.connections; + for (int i = 0, n = connections.length; i < n; i++) { + Connection connection = connections[i]; + if (connection.id == connectionID) { + connection.sendTCP(object); + break; + } + } + } + + public void sendToAllUDP (Object object) { + Connection[] connections = this.connections; + for (int i = 0, n = connections.length; i < n; i++) { + Connection connection = connections[i]; + connection.sendUDP(object); + } + } + + public void sendToAllExceptUDP (int connectionID, Object object) { + Connection[] connections = this.connections; + for (int i = 0, n = connections.length; i < n; i++) { + Connection connection = connections[i]; + if (connection.id != connectionID) connection.sendUDP(object); + } + } + + public void sendToUDP (int connectionID, Object object) { + Connection[] connections = this.connections; + for (int i = 0, n = connections.length; i < n; i++) { + Connection connection = connections[i]; + if (connection.id == connectionID) { + connection.sendUDP(object); + break; + } + } + } + + public void addListener (Listener listener) { + if (listener == null) throw new IllegalArgumentException("listener cannot be null."); + synchronized (listenerLock) { + Listener[] listeners = this.listeners; + int n = listeners.length; + for (int i = 0; i < n; i++) + if (listener == listeners[i]) return; + Listener[] newListeners = new Listener[n + 1]; + newListeners[0] = listener; + System.arraycopy(listeners, 0, newListeners, 1, n); + this.listeners = newListeners; + } + if (TRACE) trace("kryonet", "Server listener added: " + listener.getClass().getName()); + } + + public void removeListener (Listener listener) { + if (listener == null) throw new IllegalArgumentException("listener cannot be null."); + synchronized (listenerLock) { + Listener[] listeners = this.listeners; + int n = listeners.length; + Listener[] newListeners = new Listener[n - 1]; + for (int i = 0, ii = 0; i < n; i++) { + Listener copyListener = listeners[i]; + if (listener == copyListener) continue; + if (ii == n - 1) return; + newListeners[ii++] = copyListener; + } + this.listeners = newListeners; + } + if (TRACE) trace("kryonet", "Server listener removed: " + listener.getClass().getName()); + } + + /** Closes all open connections and the server port(s). */ + public void close () { + Connection[] connections = this.connections; + if (INFO && connections.length > 0) info("kryonet", "Closing server connections..."); + for (int i = 0, n = connections.length; i < n; i++) + connections[i].close(); + connections = new Connection[0]; + + ServerSocketChannel serverChannel = this.serverChannel; + if (serverChannel != null) { + try { + serverChannel.close(); + if (INFO) info("kryonet", "Server closed."); + } catch (IOException ex) { + if (DEBUG) debug("kryonet", "Unable to close server.", ex); + } + this.serverChannel = null; + } + + UdpConnection udp = this.udp; + if (udp != null) { + udp.close(); + this.udp = null; + } + + // Select one last time to complete closing the socket. + synchronized (updateLock) { + selector.wakeup(); + try { + selector.selectNow(); + } catch (IOException ignored) { + } + } + } + + public Thread getUpdateThread () { + return updateThread; + } + + /** Returns the current connections. The array returned should not be modified. */ + public Connection[] getConnections () { + return connections; + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/TcpConnection.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/TcpConnection.java similarity index 97% rename from kryonet/src/com/esotericsoftware/kryonet/TcpConnection.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/TcpConnection.java index f139805b..9056b7c2 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/TcpConnection.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/TcpConnection.java @@ -1,244 +1,244 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.net.Socket; -import java.net.SocketAddress; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.SocketChannel; - -import static com.esotericsoftware.minlog.Log.*; - -/** @author Nathan Sweet */ -class TcpConnection { - static private final int IPTOS_LOWDELAY = 0x10; - - SocketChannel socketChannel; - int keepAliveMillis = 8000; - final ByteBuffer readBuffer, writeBuffer; - boolean bufferPositionFix; - int timeoutMillis = 12000; - float idleThreshold = 0.1f; - - final Serialization serialization; - private SelectionKey selectionKey; - private long lastWriteTime, lastReadTime; - private int currentObjectLength; - private final Object writeLock = new Object(); - - public TcpConnection (Serialization serialization, int writeBufferSize, int objectBufferSize) { - this.serialization = serialization; - writeBuffer = ByteBuffer.allocate(writeBufferSize); - readBuffer = ByteBuffer.allocate(objectBufferSize); - readBuffer.flip(); - } - - public SelectionKey accept (Selector selector, SocketChannel socketChannel) throws IOException { - writeBuffer.clear(); - readBuffer.clear(); - readBuffer.flip(); - currentObjectLength = 0; - try { - this.socketChannel = socketChannel; - socketChannel.configureBlocking(false); - Socket socket = socketChannel.socket(); - socket.setTcpNoDelay(true); - - selectionKey = socketChannel.register(selector, SelectionKey.OP_READ); - - if (DEBUG) { - debug("kryonet", "Port " + socketChannel.socket().getLocalPort() + "/TCP connected to: " - + socketChannel.socket().getRemoteSocketAddress()); - } - - lastReadTime = lastWriteTime = System.currentTimeMillis(); - - return selectionKey; - } catch (IOException ex) { - close(); - throw ex; - } - } - - public void connect (Selector selector, SocketAddress remoteAddress, int timeout) throws IOException { - close(); - writeBuffer.clear(); - readBuffer.clear(); - readBuffer.flip(); - currentObjectLength = 0; - try { - SocketChannel socketChannel = selector.provider().openSocketChannel(); - Socket socket = socketChannel.socket(); - socket.setTcpNoDelay(true); - // socket.setTrafficClass(IPTOS_LOWDELAY); - socket.connect(remoteAddress, timeout); // Connect using blocking mode for simplicity. - socketChannel.configureBlocking(false); - this.socketChannel = socketChannel; - - selectionKey = socketChannel.register(selector, SelectionKey.OP_READ); - selectionKey.attach(this); - - if (DEBUG) { - debug("kryonet", "Port " + socketChannel.socket().getLocalPort() + "/TCP connected to: " - + socketChannel.socket().getRemoteSocketAddress()); - } - - lastReadTime = lastWriteTime = System.currentTimeMillis(); - } catch (IOException ex) { - close(); - IOException ioEx = new IOException("Unable to connect to: " + remoteAddress); - ioEx.initCause(ex); - throw ioEx; - } - } - - public Object readObject (Connection connection) throws IOException { - SocketChannel socketChannel = this.socketChannel; - if (socketChannel == null) throw new SocketException("Connection is closed."); - - if (currentObjectLength == 0) { - // Read the length of the next object from the socket. - int lengthLength = serialization.getLengthLength(); - if (readBuffer.remaining() < lengthLength) { - readBuffer.compact(); - int bytesRead = socketChannel.read(readBuffer); - readBuffer.flip(); - if (bytesRead == -1) throw new SocketException("Connection is closed."); - lastReadTime = System.currentTimeMillis(); - - if (readBuffer.remaining() < lengthLength) return null; - } - currentObjectLength = serialization.readLength(readBuffer); - - if (currentObjectLength <= 0) throw new KryoNetException("Invalid object length: " + currentObjectLength); - if (currentObjectLength > readBuffer.capacity()) - throw new KryoNetException("Unable to read object larger than read buffer: " + currentObjectLength); - } - - int length = currentObjectLength; - if (readBuffer.remaining() < length) { - // Fill the tcpInputStream. - readBuffer.compact(); - int bytesRead = socketChannel.read(readBuffer); - readBuffer.flip(); - if (bytesRead == -1) throw new SocketException("Connection is closed."); - lastReadTime = System.currentTimeMillis(); - - if (readBuffer.remaining() < length) return null; - } - currentObjectLength = 0; - - int startPosition = readBuffer.position(); - int oldLimit = readBuffer.limit(); - readBuffer.limit(startPosition + length); - Object object; - try { - object = serialization.read(connection, readBuffer); - } catch (Exception ex) { - throw new KryoNetException("Error during deserialization.", ex); - } - - readBuffer.limit(oldLimit); - if (readBuffer.position() - startPosition != length) - throw new KryoNetException("Incorrect number of bytes (" + (startPosition + length - readBuffer.position()) - + " remaining) used to deserialize object: " + object); - - return object; - } - - public void writeOperation () throws IOException { - synchronized (writeLock) { - if (writeToSocket()) { - // Write successful, clear OP_WRITE. - selectionKey.interestOps(SelectionKey.OP_READ); - } - lastWriteTime = System.currentTimeMillis(); - } - } - - private boolean writeToSocket () throws IOException { - SocketChannel socketChannel = this.socketChannel; - if (socketChannel == null) throw new SocketException("Connection is closed."); - - ByteBuffer buffer = writeBuffer; - buffer.flip(); - while (buffer.hasRemaining()) { - if (bufferPositionFix) { - buffer.compact(); - buffer.flip(); - } - if (socketChannel.write(buffer) == 0) break; - } - buffer.compact(); - - return buffer.position() == 0; - } - - /** This method is thread safe. */ - public int send (Connection connection, Object object) throws IOException { - SocketChannel socketChannel = this.socketChannel; - if (socketChannel == null) throw new SocketException("Connection is closed."); - synchronized (writeLock) { - // Leave room for length. - int start = writeBuffer.position(); - int lengthLength = serialization.getLengthLength(); - writeBuffer.position(writeBuffer.position() + lengthLength); - - // Write data. - try { - serialization.write(connection, writeBuffer, object); - } catch (KryoNetException ex) { - throw new KryoNetException("Error serializing object of type: " + object.getClass().getName(), ex); - } - int end = writeBuffer.position(); - - // Write data length. - writeBuffer.position(start); - serialization.writeLength(writeBuffer, end - lengthLength - start); - writeBuffer.position(end); - - // Write to socket if no data was queued. - if (start == 0 && !writeToSocket()) { - // A partial write, set OP_WRITE to be notified when more writing can occur. - selectionKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); - } else { - // Full write, wake up selector so idle event will be fired. - selectionKey.selector().wakeup(); - } - - if (DEBUG || TRACE) { - float percentage = writeBuffer.position() / (float)writeBuffer.capacity(); - if (DEBUG && percentage > 0.75f) - debug("kryonet", connection + " TCP write buffer is approaching capacity: " + percentage + "%"); - else if (TRACE && percentage > 0.25f) - trace("kryonet", connection + " TCP write buffer utilization: " + percentage + "%"); - } - - lastWriteTime = System.currentTimeMillis(); - return end - start; - } - } - - public void close () { - try { - if (socketChannel != null) { - socketChannel.close(); - socketChannel = null; - if (selectionKey != null) selectionKey.selector().wakeup(); - } - } catch (IOException ex) { - if (DEBUG) debug("kryonet", "Unable to close TCP connection.", ex); - } - } - - public boolean needsKeepAlive (long time) { - return socketChannel != null && keepAliveMillis > 0 && time - lastWriteTime > keepAliveMillis; - } - - public boolean isTimedOut (long time) { - return socketChannel != null && timeoutMillis > 0 && time - lastReadTime > timeoutMillis; - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; + +import static com.esotericsoftware.minlog.Log.*; + +/** @author Nathan Sweet */ +class TcpConnection { + static private final int IPTOS_LOWDELAY = 0x10; + + SocketChannel socketChannel; + int keepAliveMillis = 8000; + final ByteBuffer readBuffer, writeBuffer; + boolean bufferPositionFix; + int timeoutMillis = 12000; + float idleThreshold = 0.1f; + + final Serialization serialization; + private SelectionKey selectionKey; + private long lastWriteTime, lastReadTime; + private int currentObjectLength; + private final Object writeLock = new Object(); + + public TcpConnection (Serialization serialization, int writeBufferSize, int objectBufferSize) { + this.serialization = serialization; + writeBuffer = ByteBuffer.allocate(writeBufferSize); + readBuffer = ByteBuffer.allocate(objectBufferSize); + readBuffer.flip(); + } + + public SelectionKey accept (Selector selector, SocketChannel socketChannel) throws IOException { + writeBuffer.clear(); + readBuffer.clear(); + readBuffer.flip(); + currentObjectLength = 0; + try { + this.socketChannel = socketChannel; + socketChannel.configureBlocking(false); + Socket socket = socketChannel.socket(); + socket.setTcpNoDelay(true); + + selectionKey = socketChannel.register(selector, SelectionKey.OP_READ); + + if (DEBUG) { + debug("kryonet", "Port " + socketChannel.socket().getLocalPort() + "/TCP connected to: " + + socketChannel.socket().getRemoteSocketAddress()); + } + + lastReadTime = lastWriteTime = System.currentTimeMillis(); + + return selectionKey; + } catch (IOException ex) { + close(); + throw ex; + } + } + + public void connect (Selector selector, SocketAddress remoteAddress, int timeout) throws IOException { + close(); + writeBuffer.clear(); + readBuffer.clear(); + readBuffer.flip(); + currentObjectLength = 0; + try { + SocketChannel socketChannel = selector.provider().openSocketChannel(); + Socket socket = socketChannel.socket(); + socket.setTcpNoDelay(true); + // socket.setTrafficClass(IPTOS_LOWDELAY); + socket.connect(remoteAddress, timeout); // Connect using blocking mode for simplicity. + socketChannel.configureBlocking(false); + this.socketChannel = socketChannel; + + selectionKey = socketChannel.register(selector, SelectionKey.OP_READ); + selectionKey.attach(this); + + if (DEBUG) { + debug("kryonet", "Port " + socketChannel.socket().getLocalPort() + "/TCP connected to: " + + socketChannel.socket().getRemoteSocketAddress()); + } + + lastReadTime = lastWriteTime = System.currentTimeMillis(); + } catch (IOException ex) { + close(); + IOException ioEx = new IOException("Unable to connect to: " + remoteAddress); + ioEx.initCause(ex); + throw ioEx; + } + } + + public Object readObject (Connection connection) throws IOException { + SocketChannel socketChannel = this.socketChannel; + if (socketChannel == null) throw new SocketException("Connection is closed."); + + if (currentObjectLength == 0) { + // Read the length of the next object from the socket. + int lengthLength = serialization.getLengthLength(); + if (readBuffer.remaining() < lengthLength) { + readBuffer.compact(); + int bytesRead = socketChannel.read(readBuffer); + readBuffer.flip(); + if (bytesRead == -1) throw new SocketException("Connection is closed."); + lastReadTime = System.currentTimeMillis(); + + if (readBuffer.remaining() < lengthLength) return null; + } + currentObjectLength = serialization.readLength(readBuffer); + + if (currentObjectLength <= 0) throw new KryoNetException("Invalid object length: " + currentObjectLength); + if (currentObjectLength > readBuffer.capacity()) + throw new KryoNetException("Unable to read object larger than read buffer: " + currentObjectLength); + } + + int length = currentObjectLength; + if (readBuffer.remaining() < length) { + // Fill the tcpInputStream. + readBuffer.compact(); + int bytesRead = socketChannel.read(readBuffer); + readBuffer.flip(); + if (bytesRead == -1) throw new SocketException("Connection is closed."); + lastReadTime = System.currentTimeMillis(); + + if (readBuffer.remaining() < length) return null; + } + currentObjectLength = 0; + + int startPosition = readBuffer.position(); + int oldLimit = readBuffer.limit(); + readBuffer.limit(startPosition + length); + Object object; + try { + object = serialization.read(connection, readBuffer); + } catch (Exception ex) { + throw new KryoNetException("Error during deserialization.", ex); + } + + readBuffer.limit(oldLimit); + if (readBuffer.position() - startPosition != length) + throw new KryoNetException("Incorrect number of bytes (" + (startPosition + length - readBuffer.position()) + + " remaining) used to deserialize object: " + object); + + return object; + } + + public void writeOperation () throws IOException { + synchronized (writeLock) { + if (writeToSocket()) { + // Write successful, clear OP_WRITE. + selectionKey.interestOps(SelectionKey.OP_READ); + } + lastWriteTime = System.currentTimeMillis(); + } + } + + private boolean writeToSocket () throws IOException { + SocketChannel socketChannel = this.socketChannel; + if (socketChannel == null) throw new SocketException("Connection is closed."); + + ByteBuffer buffer = writeBuffer; + buffer.flip(); + while (buffer.hasRemaining()) { + if (bufferPositionFix) { + buffer.compact(); + buffer.flip(); + } + if (socketChannel.write(buffer) == 0) break; + } + buffer.compact(); + + return buffer.position() == 0; + } + + /** This method is thread safe. */ + public int send (Connection connection, Object object) throws IOException { + SocketChannel socketChannel = this.socketChannel; + if (socketChannel == null) throw new SocketException("Connection is closed."); + synchronized (writeLock) { + // Leave room for length. + int start = writeBuffer.position(); + int lengthLength = serialization.getLengthLength(); + writeBuffer.position(writeBuffer.position() + lengthLength); + + // Write data. + try { + serialization.write(connection, writeBuffer, object); + } catch (KryoNetException ex) { + throw new KryoNetException("Error serializing object of type: " + object.getClass().getName(), ex); + } + int end = writeBuffer.position(); + + // Write data length. + writeBuffer.position(start); + serialization.writeLength(writeBuffer, end - lengthLength - start); + writeBuffer.position(end); + + // Write to socket if no data was queued. + if (start == 0 && !writeToSocket()) { + // A partial write, set OP_WRITE to be notified when more writing can occur. + selectionKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); + } else { + // Full write, wake up selector so idle event will be fired. + selectionKey.selector().wakeup(); + } + + if (DEBUG || TRACE) { + float percentage = writeBuffer.position() / (float)writeBuffer.capacity(); + if (DEBUG && percentage > 0.75f) + debug("kryonet", connection + " TCP write buffer is approaching capacity: " + percentage + "%"); + else if (TRACE && percentage > 0.25f) + trace("kryonet", connection + " TCP write buffer utilization: " + percentage + "%"); + } + + lastWriteTime = System.currentTimeMillis(); + return end - start; + } + } + + public void close () { + try { + if (socketChannel != null) { + socketChannel.close(); + socketChannel = null; + if (selectionKey != null) selectionKey.selector().wakeup(); + } + } catch (IOException ex) { + if (DEBUG) debug("kryonet", "Unable to close TCP connection.", ex); + } + } + + public boolean needsKeepAlive (long time) { + return socketChannel != null && keepAliveMillis > 0 && time - lastWriteTime > keepAliveMillis; + } + + public boolean isTimedOut (long time) { + return socketChannel != null && timeoutMillis > 0 && time - lastReadTime > timeoutMillis; + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/UdpConnection.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/UdpConnection.java similarity index 96% rename from kryonet/src/com/esotericsoftware/kryonet/UdpConnection.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/UdpConnection.java index 19cfdd9b..8e923878 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/UdpConnection.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/UdpConnection.java @@ -1,137 +1,137 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.nio.channels.DatagramChannel; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; - -import static com.esotericsoftware.minlog.Log.*; - -/** @author Nathan Sweet */ -class UdpConnection { - InetSocketAddress connectedAddress; - DatagramChannel datagramChannel; - int keepAliveMillis = 19000; - final ByteBuffer readBuffer, writeBuffer; - private final Serialization serialization; - private SelectionKey selectionKey; - private final Object writeLock = new Object(); - private long lastCommunicationTime; - - public UdpConnection (Serialization serialization, int bufferSize) { - this.serialization = serialization; - readBuffer = ByteBuffer.allocate(bufferSize); - writeBuffer = ByteBuffer.allocateDirect(bufferSize); - } - - public void bind (Selector selector, InetSocketAddress localPort) throws IOException { - close(); - readBuffer.clear(); - writeBuffer.clear(); - try { - datagramChannel = selector.provider().openDatagramChannel(); - datagramChannel.socket().bind(localPort); - datagramChannel.configureBlocking(false); - selectionKey = datagramChannel.register(selector, SelectionKey.OP_READ); - - lastCommunicationTime = System.currentTimeMillis(); - } catch (IOException ex) { - close(); - throw ex; - } - } - - public void connect (Selector selector, InetSocketAddress remoteAddress) throws IOException { - close(); - readBuffer.clear(); - writeBuffer.clear(); - try { - datagramChannel = selector.provider().openDatagramChannel(); - datagramChannel.socket().bind(null); - datagramChannel.socket().connect(remoteAddress); - datagramChannel.configureBlocking(false); - - selectionKey = datagramChannel.register(selector, SelectionKey.OP_READ); - - lastCommunicationTime = System.currentTimeMillis(); - - connectedAddress = remoteAddress; - } catch (IOException ex) { - close(); - IOException ioEx = new IOException("Unable to connect to: " + remoteAddress); - ioEx.initCause(ex); - throw ioEx; - } - } - - public InetSocketAddress readFromAddress () throws IOException { - DatagramChannel datagramChannel = this.datagramChannel; - if (datagramChannel == null) throw new SocketException("Connection is closed."); - lastCommunicationTime = System.currentTimeMillis(); - return (InetSocketAddress)datagramChannel.receive(readBuffer); - } - - public Object readObject (Connection connection) { - readBuffer.flip(); - try { - try { - Object object = serialization.read(connection, readBuffer); - if (readBuffer.hasRemaining()) - throw new KryoNetException("Incorrect number of bytes (" + readBuffer.remaining() - + " remaining) used to deserialize object: " + object); - return object; - } catch (Exception ex) { - throw new KryoNetException("Error during deserialization.", ex); - } - } finally { - readBuffer.clear(); - } - } - - /** This method is thread safe. */ - public int send (Connection connection, Object object, SocketAddress address) throws IOException { - DatagramChannel datagramChannel = this.datagramChannel; - if (datagramChannel == null) throw new SocketException("Connection is closed."); - synchronized (writeLock) { - try { - try { - serialization.write(connection, writeBuffer, object); - } catch (Exception ex) { - throw new KryoNetException("Error serializing object of type: " + object.getClass().getName(), ex); - } - writeBuffer.flip(); - int length = writeBuffer.limit(); - datagramChannel.send(writeBuffer, address); - - lastCommunicationTime = System.currentTimeMillis(); - - boolean wasFullWrite = !writeBuffer.hasRemaining(); - return wasFullWrite ? length : -1; - } finally { - writeBuffer.clear(); - } - } - } - - public void close () { - connectedAddress = null; - try { - if (datagramChannel != null) { - datagramChannel.close(); - datagramChannel = null; - if (selectionKey != null) selectionKey.selector().wakeup(); - } - } catch (IOException ex) { - if (DEBUG) debug("kryonet", "Unable to close UDP connection.", ex); - } - } - - public boolean needsKeepAlive (long time) { - return connectedAddress != null && keepAliveMillis > 0 && time - lastCommunicationTime > keepAliveMillis; - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; + +import static com.esotericsoftware.minlog.Log.*; + +/** @author Nathan Sweet */ +class UdpConnection { + InetSocketAddress connectedAddress; + DatagramChannel datagramChannel; + int keepAliveMillis = 19000; + final ByteBuffer readBuffer, writeBuffer; + private final Serialization serialization; + private SelectionKey selectionKey; + private final Object writeLock = new Object(); + private long lastCommunicationTime; + + public UdpConnection (Serialization serialization, int bufferSize) { + this.serialization = serialization; + readBuffer = ByteBuffer.allocate(bufferSize); + writeBuffer = ByteBuffer.allocateDirect(bufferSize); + } + + public void bind (Selector selector, InetSocketAddress localPort) throws IOException { + close(); + readBuffer.clear(); + writeBuffer.clear(); + try { + datagramChannel = selector.provider().openDatagramChannel(); + datagramChannel.socket().bind(localPort); + datagramChannel.configureBlocking(false); + selectionKey = datagramChannel.register(selector, SelectionKey.OP_READ); + + lastCommunicationTime = System.currentTimeMillis(); + } catch (IOException ex) { + close(); + throw ex; + } + } + + public void connect (Selector selector, InetSocketAddress remoteAddress) throws IOException { + close(); + readBuffer.clear(); + writeBuffer.clear(); + try { + datagramChannel = selector.provider().openDatagramChannel(); + datagramChannel.socket().bind(null); + datagramChannel.socket().connect(remoteAddress); + datagramChannel.configureBlocking(false); + + selectionKey = datagramChannel.register(selector, SelectionKey.OP_READ); + + lastCommunicationTime = System.currentTimeMillis(); + + connectedAddress = remoteAddress; + } catch (IOException ex) { + close(); + IOException ioEx = new IOException("Unable to connect to: " + remoteAddress); + ioEx.initCause(ex); + throw ioEx; + } + } + + public InetSocketAddress readFromAddress () throws IOException { + DatagramChannel datagramChannel = this.datagramChannel; + if (datagramChannel == null) throw new SocketException("Connection is closed."); + lastCommunicationTime = System.currentTimeMillis(); + return (InetSocketAddress)datagramChannel.receive(readBuffer); + } + + public Object readObject (Connection connection) { + readBuffer.flip(); + try { + try { + Object object = serialization.read(connection, readBuffer); + if (readBuffer.hasRemaining()) + throw new KryoNetException("Incorrect number of bytes (" + readBuffer.remaining() + + " remaining) used to deserialize object: " + object); + return object; + } catch (Exception ex) { + throw new KryoNetException("Error during deserialization.", ex); + } + } finally { + readBuffer.clear(); + } + } + + /** This method is thread safe. */ + public int send (Connection connection, Object object, SocketAddress address) throws IOException { + DatagramChannel datagramChannel = this.datagramChannel; + if (datagramChannel == null) throw new SocketException("Connection is closed."); + synchronized (writeLock) { + try { + try { + serialization.write(connection, writeBuffer, object); + } catch (Exception ex) { + throw new KryoNetException("Error serializing object of type: " + object.getClass().getName(), ex); + } + writeBuffer.flip(); + int length = writeBuffer.limit(); + datagramChannel.send(writeBuffer, address); + + lastCommunicationTime = System.currentTimeMillis(); + + boolean wasFullWrite = !writeBuffer.hasRemaining(); + return wasFullWrite ? length : -1; + } finally { + writeBuffer.clear(); + } + } + } + + public void close () { + connectedAddress = null; + try { + if (datagramChannel != null) { + datagramChannel.close(); + datagramChannel = null; + if (selectionKey != null) selectionKey.selector().wakeup(); + } + } catch (IOException ex) { + if (DEBUG) debug("kryonet", "Unable to close UDP connection.", ex); + } + } + + public boolean needsKeepAlive (long time) { + return connectedAddress != null && keepAliveMillis > 0 && time - lastCommunicationTime > keepAliveMillis; + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/rmi/ObjectSpace.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/ObjectSpace.java similarity index 97% rename from kryonet/src/com/esotericsoftware/kryonet/rmi/ObjectSpace.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/ObjectSpace.java index dce41d04..3ccfe8a1 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/rmi/ObjectSpace.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/ObjectSpace.java @@ -1,624 +1,624 @@ - -package com.esotericsoftware.kryonet.rmi; - -import static com.esotericsoftware.minlog.Log.*; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.KryoException; -import com.esotericsoftware.kryo.KryoSerializable; -import com.esotericsoftware.kryo.Serializer; -import com.esotericsoftware.kryo.io.Input; -import com.esotericsoftware.kryo.io.Output; -import com.esotericsoftware.kryo.serializers.FieldSerializer; -import com.esotericsoftware.kryo.util.IntMap; -import com.esotericsoftware.kryonet.Connection; -import com.esotericsoftware.kryonet.EndPoint; -import com.esotericsoftware.kryonet.FrameworkMessage; -import com.esotericsoftware.kryonet.Listener; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.PriorityQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReentrantLock; - -/** Allows methods on objects to be invoked remotely over TCP. Objects are {@link #register(int, Object) registered} with an ID. - * The remote end of connections that have been {@link #addConnection(Connection) added} are allowed to - * {@link #getRemoteObject(Connection, int, Class) access} registered objects. - *

    - * It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a return - * value which is not {@link RemoteObject#setNonBlocking(boolean) ignored}, an extra byte is written. If the type of a parameter is - * not final (note primitives are final) then an extra byte is written for that parameter. - * @author Nathan Sweet */ -public class ObjectSpace { - static private final byte kReturnValMask = (byte)0x80; // 1000 0000 - static private final byte kReturnExMask = (byte)0x40; // 0100 0000 - - static private final Object instancesLock = new Object(); - static ObjectSpace[] instances = new ObjectSpace[0]; - static private final HashMap methodCache = new HashMap(); - - final IntMap idToObject = new IntMap(); - Connection[] connections = {}; - final Object connectionsLock = new Object(); - Executor executor; - - private final Listener invokeListener = new Listener() { - public void received (final Connection connection, Object object) { - if (!(object instanceof InvokeMethod)) return; - if (connections != null) { - int i = 0, n = connections.length; - for (; i < n; i++) - if (connection == connections[i]) break; - if (i == n) return; // The InvokeMethod message is not for a connection in this ObjectSpace. - } - final InvokeMethod invokeMethod = (InvokeMethod)object; - final Object target = idToObject.get(invokeMethod.objectID); - if (target == null) { - if (WARN) warn("kryonet", "Ignoring remote invocation request for unknown object ID: " + invokeMethod.objectID); - return; - } - if (executor == null) - invoke(connection, target, invokeMethod); - else { - executor.execute(new Runnable() { - public void run () { - invoke(connection, target, invokeMethod); - } - }); - } - } - - public void disconnected (Connection connection) { - removeConnection(connection); - } - }; - - /** Creates an ObjectSpace with no connections. Connections must be {@link #addConnection(Connection) added} to allow the remote - * end of the connections to access objects in this ObjectSpace. */ - public ObjectSpace () { - synchronized (instancesLock) { - ObjectSpace[] instances = ObjectSpace.instances; - ObjectSpace[] newInstances = new ObjectSpace[instances.length + 1]; - newInstances[0] = this; - System.arraycopy(instances, 0, newInstances, 1, instances.length); - ObjectSpace.instances = newInstances; - } - } - - /** Creates an ObjectSpace with the specified connection. More connections can be {@link #addConnection(Connection) added}. */ - public ObjectSpace (Connection connection) { - this(); - addConnection(connection); - } - - /** Sets the executor used to invoke methods when an invocation is received from a remote endpoint. By default, no executor is - * set and invocations occur on the network thread, which should not be blocked for long. - * @param executor May be null. */ - public void setExecutor (Executor executor) { - this.executor = executor; - } - - /** Registers an object to allow the remote end of the ObjectSpace's connections to access it using the specified ID. - *

    - * If a connection is added to multiple ObjectSpaces, the same object ID should not be registered in more than one of those - * ObjectSpaces. - * @see #getRemoteObject(Connection, int, Class...) */ - public void register (int objectID, Object object) { - if (object == null) throw new IllegalArgumentException("object cannot be null."); - idToObject.put(objectID, object); - if (TRACE) trace("kryonet", "Object registered with ObjectSpace as " + objectID + ": " + object); - } - - /** Removes an object. The remote end of the ObjectSpace's connections will no longer be able to access it. */ - public void remove (int objectID) { - Object object = idToObject.remove(objectID); - if (TRACE) trace("kryonet", "Object " + objectID + " removed from ObjectSpace: " + object); - } - - /** Removes an object. The remote end of the ObjectSpace's connections will no longer be able to access it. */ - public void remove (Object object) { - if (!idToObject.containsValue(object, true)) return; - int objectID = idToObject.findKey(object, true, -1); - idToObject.remove(objectID); - if (TRACE) trace("kryonet", "Object " + objectID + " removed from ObjectSpace: " + object); - } - - /** Causes this ObjectSpace to stop listening to the connections for method invocation messages. */ - public void close () { - Connection[] connections = this.connections; - for (int i = 0; i < connections.length; i++) - connections[i].removeListener(invokeListener); - - synchronized (instancesLock) { - ArrayList temp = new ArrayList(Arrays.asList(instances)); - temp.remove(this); - instances = temp.toArray(new ObjectSpace[temp.size()]); - } - - if (TRACE) trace("kryonet", "Closed ObjectSpace."); - } - - /** Allows the remote end of the specified connection to access objects registered in this ObjectSpace. */ - public void addConnection (Connection connection) { - if (connection == null) throw new IllegalArgumentException("connection cannot be null."); - - synchronized (connectionsLock) { - Connection[] newConnections = new Connection[connections.length + 1]; - newConnections[0] = connection; - System.arraycopy(connections, 0, newConnections, 1, connections.length); - connections = newConnections; - } - - connection.addListener(invokeListener); - - if (TRACE) trace("kryonet", "Added connection to ObjectSpace: " + connection); - } - - /** Removes the specified connection, it will no longer be able to access objects registered in this ObjectSpace. */ - public void removeConnection (Connection connection) { - if (connection == null) throw new IllegalArgumentException("connection cannot be null."); - - connection.removeListener(invokeListener); - - synchronized (connectionsLock) { - ArrayList temp = new ArrayList(Arrays.asList(connections)); - temp.remove(connection); - connections = temp.toArray(new Connection[temp.size()]); - } - - if (TRACE) trace("kryonet", "Removed connection from ObjectSpace: " + connection); - } - - /** Invokes the method on the object and, if necessary, sends the result back to the connection that made the invocation - * request. This method is invoked on the update thread of the {@link EndPoint} for this ObjectSpace and unless an - * {@link #setExecutor(Executor) executor} has been set. - * @param connection The remote side of this connection requested the invocation. */ - protected void invoke (Connection connection, Object target, InvokeMethod invokeMethod) { - if (DEBUG) { - String argString = ""; - if (invokeMethod.args != null) { - argString = Arrays.deepToString(invokeMethod.args); - argString = argString.substring(1, argString.length() - 1); - } - debug("kryonet", connection + " received: " + target.getClass().getSimpleName() + "#" + invokeMethod.method.getName() - + "(" + argString + ")"); - } - - byte responseID = invokeMethod.responseID; - boolean transmitReturnVal = (responseID & kReturnValMask) == kReturnValMask; - boolean transmitExceptions = (responseID & kReturnExMask) == kReturnExMask; - - Object result = null; - Method method = invokeMethod.method; - try { - result = method.invoke(target, invokeMethod.args); - // Catch exceptions caused by the Method#invoke - } catch (InvocationTargetException ex) { - if (transmitExceptions) - result = ex.getCause(); - else - throw new RuntimeException("Error invoking method: " + method.getDeclaringClass().getName() + "." + method.getName(), - ex); - } catch (Exception ex) { - throw new RuntimeException("Error invoking method: " + method.getDeclaringClass().getName() + "." + method.getName(), ex); - } - - if (responseID == 0) return; - - InvokeMethodResult invokeMethodResult = new InvokeMethodResult(); - invokeMethodResult.objectID = invokeMethod.objectID; - invokeMethodResult.responseID = responseID; - - // Do not return non-primitives if transmitReturnVal is false - if (!transmitReturnVal && !invokeMethod.method.getReturnType().isPrimitive()) { - invokeMethodResult.result = null; - } else { - invokeMethodResult.result = result; - } - - int length = connection.sendTCP(invokeMethodResult); - if (DEBUG) debug("kryonet", connection + " sent: " + result + " (" + length + ")"); - } - - /** Identical to {@link #getRemoteObject(Connection, int, Class...)} except returns the object cast to the specified interface - * type. The returned object still implements {@link RemoteObject}. */ - static public T getRemoteObject (final Connection connection, int objectID, Class iface) { - return (T)getRemoteObject(connection, objectID, new Class[] {iface}); - } - - /** Returns a proxy object that implements the specified interfaces. Methods invoked on the proxy object will be invoked - * remotely on the object with the specified ID in the ObjectSpace for the specified connection. If the remote end of the - * connection has not {@link #addConnection(Connection) added} the connection to the ObjectSpace, the remote method invocations - * will be ignored. - *

    - * Methods that return a value will throw {@link TimeoutException} if the response is not received with the - * {@link RemoteObject#setResponseTimeout(int) response timeout}. - *

    - * If {@link RemoteObject#setNonBlocking(boolean) non-blocking} is false (the default), then methods that return a value must - * not be called from the update thread for the connection. An exception will be thrown if this occurs. Methods with a void - * return value can be called on the update thread. - *

    - * If a proxy returned from this method is part of an object graph sent over the network, the object graph on the receiving - * side will have the proxy object replaced with the registered object. - * @see RemoteObject */ - static public RemoteObject getRemoteObject (Connection connection, int objectID, Class... ifaces) { - if (connection == null) throw new IllegalArgumentException("connection cannot be null."); - if (ifaces == null) throw new IllegalArgumentException("ifaces cannot be null."); - Class[] temp = new Class[ifaces.length + 1]; - temp[0] = RemoteObject.class; - System.arraycopy(ifaces, 0, temp, 1, ifaces.length); - return (RemoteObject)Proxy.newProxyInstance(ObjectSpace.class.getClassLoader(), temp, new RemoteInvocationHandler( - connection, objectID)); - } - - /** Handles network communication when methods are invoked on a proxy. */ - static private class RemoteInvocationHandler implements InvocationHandler { - private final Connection connection; - final int objectID; - private int timeoutMillis = 3000; - private boolean nonBlocking = false; - private boolean transmitReturnValue = true; - private boolean transmitExceptions = true; - private Byte lastResponseID; - private byte nextResponseNum = 1; - private Listener responseListener; - - final ReentrantLock lock = new ReentrantLock(); - final Condition responseCondition = lock.newCondition(); - final ConcurrentHashMap responseTable = new ConcurrentHashMap(); - - public RemoteInvocationHandler (Connection connection, final int objectID) { - super(); - this.connection = connection; - this.objectID = objectID; - - responseListener = new Listener() { - public void received (Connection connection, Object object) { - if (!(object instanceof InvokeMethodResult)) return; - InvokeMethodResult invokeMethodResult = (InvokeMethodResult)object; - if (invokeMethodResult.objectID != objectID) return; - - responseTable.put(invokeMethodResult.responseID, invokeMethodResult); - - lock.lock(); - try { - responseCondition.signalAll(); - } finally { - lock.unlock(); - } - } - - public void disconnected (Connection connection) { - close(); - } - }; - connection.addListener(responseListener); - } - - public Object invoke (Object proxy, Method method, Object[] args) throws Exception { - if (method.getDeclaringClass() == RemoteObject.class) { - String name = method.getName(); - if (name.equals("close")) { - close(); - return null; - } else if (name.equals("setResponseTimeout")) { - timeoutMillis = (Integer)args[0]; - return null; - } else if (name.equals("setNonBlocking")) { - nonBlocking = (Boolean)args[0]; - return null; - } else if (name.equals("setTransmitReturnValue")) { - transmitReturnValue = (Boolean)args[0]; - return null; - } else if (name.equals("setTransmitExceptions")) { - transmitExceptions = (Boolean)args[0]; - return null; - } else if (name.equals("waitForLastResponse")) { - if (lastResponseID == null) throw new IllegalStateException("There is no last response to wait for."); - return waitForResponse(lastResponseID); - } else if (name.equals("getLastResponseID")) { - if (lastResponseID == null) throw new IllegalStateException("There is no last response ID."); - return lastResponseID; - } else if (name.equals("waitForResponse")) { - if (!transmitReturnValue && !transmitExceptions && nonBlocking) - throw new IllegalStateException("This RemoteObject is currently set to ignore all responses."); - return waitForResponse((Byte)args[0]); - } else if (name.equals("getConnection")) { - return connection; - } else { - // Should never happen, for debugging purposes only - throw new RuntimeException("Invocation handler could not find RemoteObject method. Check ObjectSpace.java"); - } - } else if (method.getDeclaringClass() == Object.class) { - if (method.getName().equals("toString")) return ""; - try { - return method.invoke(proxy, args); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - InvokeMethod invokeMethod = new InvokeMethod(); - invokeMethod.objectID = objectID; - invokeMethod.method = method; - invokeMethod.args = args; - - // The only time a invocation doesn't need a response is if it's async - // and no return values or exceptions are wanted back. - boolean needsResponse = transmitReturnValue || transmitExceptions || !nonBlocking; - if (needsResponse) { - byte responseID; - synchronized (this) { - // Increment the response counter and put it into the first six bits of the responseID byte - responseID = nextResponseNum++; - if (nextResponseNum == 64) nextResponseNum = 1; // Keep number under 2^6, avoid 0 (see else statement below) - } - // Pack return value and exception info into the top two bits - if (transmitReturnValue) responseID |= kReturnValMask; - if (transmitExceptions) responseID |= kReturnExMask; - invokeMethod.responseID = responseID; - } else { - invokeMethod.responseID = 0; // A response info of 0 means to not respond - } - int length = connection.sendTCP(invokeMethod); - if (DEBUG) { - String argString = ""; - if (args != null) { - argString = Arrays.deepToString(args); - argString = argString.substring(1, argString.length() - 1); - } - debug("kryonet", connection + " sent: " + method.getDeclaringClass().getSimpleName() + "#" + method.getName() + "(" - + argString + ") (" + length + ")"); - } - - if (invokeMethod.responseID != 0) lastResponseID = invokeMethod.responseID; - if (nonBlocking) { - Class returnType = method.getReturnType(); - if (returnType.isPrimitive()) { - if (returnType == int.class) return 0; - if (returnType == boolean.class) return Boolean.FALSE; - if (returnType == float.class) return 0f; - if (returnType == char.class) return (char)0; - if (returnType == long.class) return 0l; - if (returnType == short.class) return (short)0; - if (returnType == byte.class) return (byte)0; - if (returnType == double.class) return 0d; - } - return null; - } - try { - Object result = waitForResponse(invokeMethod.responseID); - if (result != null && result instanceof Exception) - throw (Exception)result; - else - return result; - } catch (TimeoutException ex) { - throw new TimeoutException("Response timed out: " + method.getDeclaringClass().getName() + "." + method.getName()); - } - } - - private Object waitForResponse (byte responseID) { - if (connection.getEndPoint().getUpdateThread() == Thread.currentThread()) - throw new IllegalStateException("Cannot wait for an RMI response on the connection's update thread."); - - long endTime = System.currentTimeMillis() + timeoutMillis; - - while (true) { - long remaining = endTime - System.currentTimeMillis(); - if (responseTable.containsKey(responseID)) { - InvokeMethodResult invokeMethodResult = responseTable.get(responseID); - responseTable.remove(responseID); - lastResponseID = null; - return invokeMethodResult.result; - } else { - if (remaining <= 0) throw new TimeoutException("Response timed out."); - - lock.lock(); - try { - responseCondition.await(remaining, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } finally { - lock.unlock(); - } - } - } - } - - void close () { - connection.removeListener(responseListener); - } - } - - /** Internal message to invoke methods remotely. */ - static public class InvokeMethod implements FrameworkMessage, KryoSerializable { - public int objectID; - public Method method; - public Object[] args; - // The top two bytes of the ID indicate if the remote invocation should respond with return values and exceptions, - // respectively. The rest is a six bit counter. This means up to 63 responses can be stored before undefined behavior - // occurs due to possible duplicate IDs. - public byte responseID; - - public void write (Kryo kryo, Output output) { - output.writeInt(objectID, true); - - int methodClassID = kryo.getRegistration(method.getDeclaringClass()).getId(); - output.writeInt(methodClassID, true); - - CachedMethod[] cachedMethods = getMethods(kryo, method.getDeclaringClass()); - CachedMethod cachedMethod = null; - for (int i = 0, n = cachedMethods.length; i < n; i++) { - cachedMethod = cachedMethods[i]; - if (cachedMethod.method.equals(method)) { - output.writeByte(i); - break; - } - } - - for (int i = 0, n = cachedMethod.serializers.length; i < n; i++) { - Serializer serializer = cachedMethod.serializers[i]; - if (serializer != null) - kryo.writeObjectOrNull(output, args[i], serializer); - else - kryo.writeClassAndObject(output, args[i]); - } - - output.writeByte(responseID); - } - - public void read (Kryo kryo, Input input) { - objectID = input.readInt(true); - - int methodClassID = input.readInt(true); - Class methodClass = kryo.getRegistration(methodClassID).getType(); - byte methodIndex = input.readByte(); - CachedMethod cachedMethod; - try { - cachedMethod = getMethods(kryo, methodClass)[methodIndex]; - } catch (IndexOutOfBoundsException ex) { - throw new KryoException("Invalid method index " + methodIndex + " for class: " + methodClass.getName()); - } - method = cachedMethod.method; - - args = new Object[cachedMethod.serializers.length]; - for (int i = 0, n = args.length; i < n; i++) { - Serializer serializer = cachedMethod.serializers[i]; - if (serializer != null) - args[i] = kryo.readObjectOrNull(input, method.getParameterTypes()[i], serializer); - else - args[i] = kryo.readClassAndObject(input); - } - - responseID = input.readByte(); - } - } - - /** Internal message to return the result of a remotely invoked method. */ - static public class InvokeMethodResult implements FrameworkMessage { - public int objectID; - public byte responseID; - public Object result; - } - - static CachedMethod[] getMethods (Kryo kryo, Class type) { - CachedMethod[] cachedMethods = methodCache.get(type); - if (cachedMethods != null) return cachedMethods; - - ArrayList allMethods = new ArrayList(); - Class nextClass = type; - while (nextClass != null && nextClass != Object.class) { - Collections.addAll(allMethods, nextClass.getDeclaredMethods()); - nextClass = nextClass.getSuperclass(); - } - PriorityQueue methods = new PriorityQueue(Math.max(1, allMethods.size()), new Comparator() { - public int compare (Method o1, Method o2) { - // Methods are sorted so they can be represented as an index. - int diff = o1.getName().compareTo(o2.getName()); - if (diff != 0) return diff; - Class[] argTypes1 = o1.getParameterTypes(); - Class[] argTypes2 = o2.getParameterTypes(); - if (argTypes1.length > argTypes2.length) return 1; - if (argTypes1.length < argTypes2.length) return -1; - for (int i = 0; i < argTypes1.length; i++) { - diff = argTypes1[i].getName().compareTo(argTypes2[i].getName()); - if (diff != 0) return diff; - } - throw new RuntimeException("Two methods with same signature!"); // Impossible. - } - }); - for (int i = 0, n = allMethods.size(); i < n; i++) { - Method method = allMethods.get(i); - int modifiers = method.getModifiers(); - if (Modifier.isStatic(modifiers)) continue; - if (Modifier.isPrivate(modifiers)) continue; - if (method.isSynthetic()) continue; - methods.add(method); - } - - int n = methods.size(); - cachedMethods = new CachedMethod[n]; - for (int i = 0; i < n; i++) { - CachedMethod cachedMethod = new CachedMethod(); - cachedMethod.method = methods.poll(); - - // Store the serializer for each final parameter. - Class[] parameterTypes = cachedMethod.method.getParameterTypes(); - cachedMethod.serializers = new Serializer[parameterTypes.length]; - for (int ii = 0, nn = parameterTypes.length; ii < nn; ii++) - if (kryo.isFinal(parameterTypes[ii])) cachedMethod.serializers[ii] = kryo.getSerializer(parameterTypes[ii]); - - cachedMethods[i] = cachedMethod; - } - methodCache.put(type, cachedMethods); - return cachedMethods; - } - - /** Returns the first object registered with the specified ID in any of the ObjectSpaces the specified connection belongs to. */ - static Object getRegisteredObject (Connection connection, int objectID) { - ObjectSpace[] instances = ObjectSpace.instances; - for (int i = 0, n = instances.length; i < n; i++) { - ObjectSpace objectSpace = instances[i]; - // Check if the connection is in this ObjectSpace. - Connection[] connections = objectSpace.connections; - for (int j = 0; j < connections.length; j++) { - if (connections[j] != connection) continue; - // Find an object with the objectID. - Object object = objectSpace.idToObject.get(objectID); - if (object != null) return object; - } - } - return null; - } - - /** Registers the classes needed to use ObjectSpaces. This should be called before any connections are opened. - * @see Kryo#register(Class, Serializer) */ - static public void registerClasses (final Kryo kryo) { - kryo.register(Object[].class); - kryo.register(InvokeMethod.class); - - FieldSerializer serializer = (FieldSerializer)kryo.register(InvokeMethodResult.class).getSerializer(); - serializer.getField("objectID").setClass(int.class, new Serializer() { - public void write (Kryo kryo, Output output, Integer object) { - output.writeInt(object, true); - } - - public Integer read (Kryo kryo, Input input, Class type) { - return input.readInt(true); - } - }); - - kryo.register(InvocationHandler.class, new Serializer() { - public void write (Kryo kryo, Output output, Object object) { - RemoteInvocationHandler handler = (RemoteInvocationHandler)Proxy.getInvocationHandler(object); - output.writeInt(handler.objectID, true); - } - - public Object read (Kryo kryo, Input input, Class type) { - int objectID = input.readInt(true); - Connection connection = (Connection)kryo.getContext().get("connection"); - Object object = getRegisteredObject(connection, objectID); - if (WARN && object == null) warn("kryonet", "Unknown object ID " + objectID + " for connection: " + connection); - return object; - } - }); - } - - static class CachedMethod { - Method method; - Serializer[] serializers; - } -} + +package com.esotericsoftware.kryonet.rmi; + +import static com.esotericsoftware.minlog.Log.*; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.KryoException; +import com.esotericsoftware.kryo.KryoSerializable; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryo.serializers.FieldSerializer; +import com.esotericsoftware.kryo.util.IntMap; +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.EndPoint; +import com.esotericsoftware.kryonet.FrameworkMessage; +import com.esotericsoftware.kryonet.Listener; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.PriorityQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** Allows methods on objects to be invoked remotely over TCP. Objects are {@link #register(int, Object) registered} with an ID. + * The remote end of connections that have been {@link #addConnection(Connection) added} are allowed to + * {@link #getRemoteObject(Connection, int, Class) access} registered objects. + *

    + * It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a return + * value which is not {@link RemoteObject#setNonBlocking(boolean) ignored}, an extra byte is written. If the type of a parameter is + * not final (note primitives are final) then an extra byte is written for that parameter. + * @author Nathan Sweet */ +public class ObjectSpace { + static private final byte kReturnValMask = (byte)0x80; // 1000 0000 + static private final byte kReturnExMask = (byte)0x40; // 0100 0000 + + static private final Object instancesLock = new Object(); + static ObjectSpace[] instances = new ObjectSpace[0]; + static private final HashMap methodCache = new HashMap(); + + final IntMap idToObject = new IntMap(); + Connection[] connections = {}; + final Object connectionsLock = new Object(); + Executor executor; + + private final Listener invokeListener = new Listener() { + public void received (final Connection connection, Object object) { + if (!(object instanceof InvokeMethod)) return; + if (connections != null) { + int i = 0, n = connections.length; + for (; i < n; i++) + if (connection == connections[i]) break; + if (i == n) return; // The InvokeMethod message is not for a connection in this ObjectSpace. + } + final InvokeMethod invokeMethod = (InvokeMethod)object; + final Object target = idToObject.get(invokeMethod.objectID); + if (target == null) { + if (WARN) warn("kryonet", "Ignoring remote invocation request for unknown object ID: " + invokeMethod.objectID); + return; + } + if (executor == null) + invoke(connection, target, invokeMethod); + else { + executor.execute(new Runnable() { + public void run () { + invoke(connection, target, invokeMethod); + } + }); + } + } + + public void disconnected (Connection connection) { + removeConnection(connection); + } + }; + + /** Creates an ObjectSpace with no connections. Connections must be {@link #addConnection(Connection) added} to allow the remote + * end of the connections to access objects in this ObjectSpace. */ + public ObjectSpace () { + synchronized (instancesLock) { + ObjectSpace[] instances = ObjectSpace.instances; + ObjectSpace[] newInstances = new ObjectSpace[instances.length + 1]; + newInstances[0] = this; + System.arraycopy(instances, 0, newInstances, 1, instances.length); + ObjectSpace.instances = newInstances; + } + } + + /** Creates an ObjectSpace with the specified connection. More connections can be {@link #addConnection(Connection) added}. */ + public ObjectSpace (Connection connection) { + this(); + addConnection(connection); + } + + /** Sets the executor used to invoke methods when an invocation is received from a remote endpoint. By default, no executor is + * set and invocations occur on the network thread, which should not be blocked for long. + * @param executor May be null. */ + public void setExecutor (Executor executor) { + this.executor = executor; + } + + /** Registers an object to allow the remote end of the ObjectSpace's connections to access it using the specified ID. + *

    + * If a connection is added to multiple ObjectSpaces, the same object ID should not be registered in more than one of those + * ObjectSpaces. + * @see #getRemoteObject(Connection, int, Class...) */ + public void register (int objectID, Object object) { + if (object == null) throw new IllegalArgumentException("object cannot be null."); + idToObject.put(objectID, object); + if (TRACE) trace("kryonet", "Object registered with ObjectSpace as " + objectID + ": " + object); + } + + /** Removes an object. The remote end of the ObjectSpace's connections will no longer be able to access it. */ + public void remove (int objectID) { + Object object = idToObject.remove(objectID); + if (TRACE) trace("kryonet", "Object " + objectID + " removed from ObjectSpace: " + object); + } + + /** Removes an object. The remote end of the ObjectSpace's connections will no longer be able to access it. */ + public void remove (Object object) { + if (!idToObject.containsValue(object, true)) return; + int objectID = idToObject.findKey(object, true, -1); + idToObject.remove(objectID); + if (TRACE) trace("kryonet", "Object " + objectID + " removed from ObjectSpace: " + object); + } + + /** Causes this ObjectSpace to stop listening to the connections for method invocation messages. */ + public void close () { + Connection[] connections = this.connections; + for (int i = 0; i < connections.length; i++) + connections[i].removeListener(invokeListener); + + synchronized (instancesLock) { + ArrayList temp = new ArrayList(Arrays.asList(instances)); + temp.remove(this); + instances = temp.toArray(new ObjectSpace[temp.size()]); + } + + if (TRACE) trace("kryonet", "Closed ObjectSpace."); + } + + /** Allows the remote end of the specified connection to access objects registered in this ObjectSpace. */ + public void addConnection (Connection connection) { + if (connection == null) throw new IllegalArgumentException("connection cannot be null."); + + synchronized (connectionsLock) { + Connection[] newConnections = new Connection[connections.length + 1]; + newConnections[0] = connection; + System.arraycopy(connections, 0, newConnections, 1, connections.length); + connections = newConnections; + } + + connection.addListener(invokeListener); + + if (TRACE) trace("kryonet", "Added connection to ObjectSpace: " + connection); + } + + /** Removes the specified connection, it will no longer be able to access objects registered in this ObjectSpace. */ + public void removeConnection (Connection connection) { + if (connection == null) throw new IllegalArgumentException("connection cannot be null."); + + connection.removeListener(invokeListener); + + synchronized (connectionsLock) { + ArrayList temp = new ArrayList(Arrays.asList(connections)); + temp.remove(connection); + connections = temp.toArray(new Connection[temp.size()]); + } + + if (TRACE) trace("kryonet", "Removed connection from ObjectSpace: " + connection); + } + + /** Invokes the method on the object and, if necessary, sends the result back to the connection that made the invocation + * request. This method is invoked on the update thread of the {@link EndPoint} for this ObjectSpace and unless an + * {@link #setExecutor(Executor) executor} has been set. + * @param connection The remote side of this connection requested the invocation. */ + protected void invoke (Connection connection, Object target, InvokeMethod invokeMethod) { + if (DEBUG) { + String argString = ""; + if (invokeMethod.args != null) { + argString = Arrays.deepToString(invokeMethod.args); + argString = argString.substring(1, argString.length() - 1); + } + debug("kryonet", connection + " received: " + target.getClass().getSimpleName() + "#" + invokeMethod.method.getName() + + "(" + argString + ")"); + } + + byte responseID = invokeMethod.responseID; + boolean transmitReturnVal = (responseID & kReturnValMask) == kReturnValMask; + boolean transmitExceptions = (responseID & kReturnExMask) == kReturnExMask; + + Object result = null; + Method method = invokeMethod.method; + try { + result = method.invoke(target, invokeMethod.args); + // Catch exceptions caused by the Method#invoke + } catch (InvocationTargetException ex) { + if (transmitExceptions) + result = ex.getCause(); + else + throw new RuntimeException("Error invoking method: " + method.getDeclaringClass().getName() + "." + method.getName(), + ex); + } catch (Exception ex) { + throw new RuntimeException("Error invoking method: " + method.getDeclaringClass().getName() + "." + method.getName(), ex); + } + + if (responseID == 0) return; + + InvokeMethodResult invokeMethodResult = new InvokeMethodResult(); + invokeMethodResult.objectID = invokeMethod.objectID; + invokeMethodResult.responseID = responseID; + + // Do not return non-primitives if transmitReturnVal is false + if (!transmitReturnVal && !invokeMethod.method.getReturnType().isPrimitive()) { + invokeMethodResult.result = null; + } else { + invokeMethodResult.result = result; + } + + int length = connection.sendTCP(invokeMethodResult); + if (DEBUG) debug("kryonet", connection + " sent: " + result + " (" + length + ")"); + } + + /** Identical to {@link #getRemoteObject(Connection, int, Class...)} except returns the object cast to the specified interface + * type. The returned object still implements {@link RemoteObject}. */ + static public T getRemoteObject (final Connection connection, int objectID, Class iface) { + return (T)getRemoteObject(connection, objectID, new Class[] {iface}); + } + + /** Returns a proxy object that implements the specified interfaces. Methods invoked on the proxy object will be invoked + * remotely on the object with the specified ID in the ObjectSpace for the specified connection. If the remote end of the + * connection has not {@link #addConnection(Connection) added} the connection to the ObjectSpace, the remote method invocations + * will be ignored. + *

    + * Methods that return a value will throw {@link TimeoutException} if the response is not received with the + * {@link RemoteObject#setResponseTimeout(int) response timeout}. + *

    + * If {@link RemoteObject#setNonBlocking(boolean) non-blocking} is false (the default), then methods that return a value must + * not be called from the update thread for the connection. An exception will be thrown if this occurs. Methods with a void + * return value can be called on the update thread. + *

    + * If a proxy returned from this method is part of an object graph sent over the network, the object graph on the receiving + * side will have the proxy object replaced with the registered object. + * @see RemoteObject */ + static public RemoteObject getRemoteObject (Connection connection, int objectID, Class... ifaces) { + if (connection == null) throw new IllegalArgumentException("connection cannot be null."); + if (ifaces == null) throw new IllegalArgumentException("ifaces cannot be null."); + Class[] temp = new Class[ifaces.length + 1]; + temp[0] = RemoteObject.class; + System.arraycopy(ifaces, 0, temp, 1, ifaces.length); + return (RemoteObject)Proxy.newProxyInstance(ObjectSpace.class.getClassLoader(), temp, new RemoteInvocationHandler( + connection, objectID)); + } + + /** Handles network communication when methods are invoked on a proxy. */ + static private class RemoteInvocationHandler implements InvocationHandler { + private final Connection connection; + final int objectID; + private int timeoutMillis = 3000; + private boolean nonBlocking = false; + private boolean transmitReturnValue = true; + private boolean transmitExceptions = true; + private Byte lastResponseID; + private byte nextResponseNum = 1; + private Listener responseListener; + + final ReentrantLock lock = new ReentrantLock(); + final Condition responseCondition = lock.newCondition(); + final ConcurrentHashMap responseTable = new ConcurrentHashMap(); + + public RemoteInvocationHandler (Connection connection, final int objectID) { + super(); + this.connection = connection; + this.objectID = objectID; + + responseListener = new Listener() { + public void received (Connection connection, Object object) { + if (!(object instanceof InvokeMethodResult)) return; + InvokeMethodResult invokeMethodResult = (InvokeMethodResult)object; + if (invokeMethodResult.objectID != objectID) return; + + responseTable.put(invokeMethodResult.responseID, invokeMethodResult); + + lock.lock(); + try { + responseCondition.signalAll(); + } finally { + lock.unlock(); + } + } + + public void disconnected (Connection connection) { + close(); + } + }; + connection.addListener(responseListener); + } + + public Object invoke (Object proxy, Method method, Object[] args) throws Exception { + if (method.getDeclaringClass() == RemoteObject.class) { + String name = method.getName(); + if (name.equals("close")) { + close(); + return null; + } else if (name.equals("setResponseTimeout")) { + timeoutMillis = (Integer)args[0]; + return null; + } else if (name.equals("setNonBlocking")) { + nonBlocking = (Boolean)args[0]; + return null; + } else if (name.equals("setTransmitReturnValue")) { + transmitReturnValue = (Boolean)args[0]; + return null; + } else if (name.equals("setTransmitExceptions")) { + transmitExceptions = (Boolean)args[0]; + return null; + } else if (name.equals("waitForLastResponse")) { + if (lastResponseID == null) throw new IllegalStateException("There is no last response to wait for."); + return waitForResponse(lastResponseID); + } else if (name.equals("getLastResponseID")) { + if (lastResponseID == null) throw new IllegalStateException("There is no last response ID."); + return lastResponseID; + } else if (name.equals("waitForResponse")) { + if (!transmitReturnValue && !transmitExceptions && nonBlocking) + throw new IllegalStateException("This RemoteObject is currently set to ignore all responses."); + return waitForResponse((Byte)args[0]); + } else if (name.equals("getConnection")) { + return connection; + } else { + // Should never happen, for debugging purposes only + throw new RuntimeException("Invocation handler could not find RemoteObject method. Check ObjectSpace.java"); + } + } else if (method.getDeclaringClass() == Object.class) { + if (method.getName().equals("toString")) return ""; + try { + return method.invoke(proxy, args); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + InvokeMethod invokeMethod = new InvokeMethod(); + invokeMethod.objectID = objectID; + invokeMethod.method = method; + invokeMethod.args = args; + + // The only time a invocation doesn't need a response is if it's async + // and no return values or exceptions are wanted back. + boolean needsResponse = transmitReturnValue || transmitExceptions || !nonBlocking; + if (needsResponse) { + byte responseID; + synchronized (this) { + // Increment the response counter and put it into the first six bits of the responseID byte + responseID = nextResponseNum++; + if (nextResponseNum == 64) nextResponseNum = 1; // Keep number under 2^6, avoid 0 (see else statement below) + } + // Pack return value and exception info into the top two bits + if (transmitReturnValue) responseID |= kReturnValMask; + if (transmitExceptions) responseID |= kReturnExMask; + invokeMethod.responseID = responseID; + } else { + invokeMethod.responseID = 0; // A response info of 0 means to not respond + } + int length = connection.sendTCP(invokeMethod); + if (DEBUG) { + String argString = ""; + if (args != null) { + argString = Arrays.deepToString(args); + argString = argString.substring(1, argString.length() - 1); + } + debug("kryonet", connection + " sent: " + method.getDeclaringClass().getSimpleName() + "#" + method.getName() + "(" + + argString + ") (" + length + ")"); + } + + if (invokeMethod.responseID != 0) lastResponseID = invokeMethod.responseID; + if (nonBlocking) { + Class returnType = method.getReturnType(); + if (returnType.isPrimitive()) { + if (returnType == int.class) return 0; + if (returnType == boolean.class) return Boolean.FALSE; + if (returnType == float.class) return 0f; + if (returnType == char.class) return (char)0; + if (returnType == long.class) return 0l; + if (returnType == short.class) return (short)0; + if (returnType == byte.class) return (byte)0; + if (returnType == double.class) return 0d; + } + return null; + } + try { + Object result = waitForResponse(invokeMethod.responseID); + if (result != null && result instanceof Exception) + throw (Exception)result; + else + return result; + } catch (TimeoutException ex) { + throw new TimeoutException("Response timed out: " + method.getDeclaringClass().getName() + "." + method.getName()); + } + } + + private Object waitForResponse (byte responseID) { + if (connection.getEndPoint().getUpdateThread() == Thread.currentThread()) + throw new IllegalStateException("Cannot wait for an RMI response on the connection's update thread."); + + long endTime = System.currentTimeMillis() + timeoutMillis; + + while (true) { + long remaining = endTime - System.currentTimeMillis(); + if (responseTable.containsKey(responseID)) { + InvokeMethodResult invokeMethodResult = responseTable.get(responseID); + responseTable.remove(responseID); + lastResponseID = null; + return invokeMethodResult.result; + } else { + if (remaining <= 0) throw new TimeoutException("Response timed out."); + + lock.lock(); + try { + responseCondition.await(remaining, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } finally { + lock.unlock(); + } + } + } + } + + void close () { + connection.removeListener(responseListener); + } + } + + /** Internal message to invoke methods remotely. */ + static public class InvokeMethod implements FrameworkMessage, KryoSerializable { + public int objectID; + public Method method; + public Object[] args; + // The top two bytes of the ID indicate if the remote invocation should respond with return values and exceptions, + // respectively. The rest is a six bit counter. This means up to 63 responses can be stored before undefined behavior + // occurs due to possible duplicate IDs. + public byte responseID; + + public void write (Kryo kryo, Output output) { + output.writeInt(objectID, true); + + int methodClassID = kryo.getRegistration(method.getDeclaringClass()).getId(); + output.writeInt(methodClassID, true); + + CachedMethod[] cachedMethods = getMethods(kryo, method.getDeclaringClass()); + CachedMethod cachedMethod = null; + for (int i = 0, n = cachedMethods.length; i < n; i++) { + cachedMethod = cachedMethods[i]; + if (cachedMethod.method.equals(method)) { + output.writeByte(i); + break; + } + } + + for (int i = 0, n = cachedMethod.serializers.length; i < n; i++) { + Serializer serializer = cachedMethod.serializers[i]; + if (serializer != null) + kryo.writeObjectOrNull(output, args[i], serializer); + else + kryo.writeClassAndObject(output, args[i]); + } + + output.writeByte(responseID); + } + + public void read (Kryo kryo, Input input) { + objectID = input.readInt(true); + + int methodClassID = input.readInt(true); + Class methodClass = kryo.getRegistration(methodClassID).getType(); + byte methodIndex = input.readByte(); + CachedMethod cachedMethod; + try { + cachedMethod = getMethods(kryo, methodClass)[methodIndex]; + } catch (IndexOutOfBoundsException ex) { + throw new KryoException("Invalid method index " + methodIndex + " for class: " + methodClass.getName()); + } + method = cachedMethod.method; + + args = new Object[cachedMethod.serializers.length]; + for (int i = 0, n = args.length; i < n; i++) { + Serializer serializer = cachedMethod.serializers[i]; + if (serializer != null) + args[i] = kryo.readObjectOrNull(input, method.getParameterTypes()[i], serializer); + else + args[i] = kryo.readClassAndObject(input); + } + + responseID = input.readByte(); + } + } + + /** Internal message to return the result of a remotely invoked method. */ + static public class InvokeMethodResult implements FrameworkMessage { + public int objectID; + public byte responseID; + public Object result; + } + + static CachedMethod[] getMethods (Kryo kryo, Class type) { + CachedMethod[] cachedMethods = methodCache.get(type); + if (cachedMethods != null) return cachedMethods; + + ArrayList allMethods = new ArrayList(); + Class nextClass = type; + while (nextClass != null && nextClass != Object.class) { + Collections.addAll(allMethods, nextClass.getDeclaredMethods()); + nextClass = nextClass.getSuperclass(); + } + PriorityQueue methods = new PriorityQueue(Math.max(1, allMethods.size()), new Comparator() { + public int compare (Method o1, Method o2) { + // Methods are sorted so they can be represented as an index. + int diff = o1.getName().compareTo(o2.getName()); + if (diff != 0) return diff; + Class[] argTypes1 = o1.getParameterTypes(); + Class[] argTypes2 = o2.getParameterTypes(); + if (argTypes1.length > argTypes2.length) return 1; + if (argTypes1.length < argTypes2.length) return -1; + for (int i = 0; i < argTypes1.length; i++) { + diff = argTypes1[i].getName().compareTo(argTypes2[i].getName()); + if (diff != 0) return diff; + } + throw new RuntimeException("Two methods with same signature!"); // Impossible. + } + }); + for (int i = 0, n = allMethods.size(); i < n; i++) { + Method method = allMethods.get(i); + int modifiers = method.getModifiers(); + if (Modifier.isStatic(modifiers)) continue; + if (Modifier.isPrivate(modifiers)) continue; + if (method.isSynthetic()) continue; + methods.add(method); + } + + int n = methods.size(); + cachedMethods = new CachedMethod[n]; + for (int i = 0; i < n; i++) { + CachedMethod cachedMethod = new CachedMethod(); + cachedMethod.method = methods.poll(); + + // Store the serializer for each final parameter. + Class[] parameterTypes = cachedMethod.method.getParameterTypes(); + cachedMethod.serializers = new Serializer[parameterTypes.length]; + for (int ii = 0, nn = parameterTypes.length; ii < nn; ii++) + if (kryo.isFinal(parameterTypes[ii])) cachedMethod.serializers[ii] = kryo.getSerializer(parameterTypes[ii]); + + cachedMethods[i] = cachedMethod; + } + methodCache.put(type, cachedMethods); + return cachedMethods; + } + + /** Returns the first object registered with the specified ID in any of the ObjectSpaces the specified connection belongs to. */ + static Object getRegisteredObject (Connection connection, int objectID) { + ObjectSpace[] instances = ObjectSpace.instances; + for (int i = 0, n = instances.length; i < n; i++) { + ObjectSpace objectSpace = instances[i]; + // Check if the connection is in this ObjectSpace. + Connection[] connections = objectSpace.connections; + for (int j = 0; j < connections.length; j++) { + if (connections[j] != connection) continue; + // Find an object with the objectID. + Object object = objectSpace.idToObject.get(objectID); + if (object != null) return object; + } + } + return null; + } + + /** Registers the classes needed to use ObjectSpaces. This should be called before any connections are opened. + * @see Kryo#register(Class, Serializer) */ + static public void registerClasses (final Kryo kryo) { + kryo.register(Object[].class); + kryo.register(InvokeMethod.class); + + FieldSerializer serializer = (FieldSerializer)kryo.register(InvokeMethodResult.class).getSerializer(); + serializer.getField("objectID").setClass(int.class, new Serializer() { + public void write (Kryo kryo, Output output, Integer object) { + output.writeInt(object, true); + } + + public Integer read (Kryo kryo, Input input, Class type) { + return input.readInt(true); + } + }); + + kryo.register(InvocationHandler.class, new Serializer() { + public void write (Kryo kryo, Output output, Object object) { + RemoteInvocationHandler handler = (RemoteInvocationHandler)Proxy.getInvocationHandler(object); + output.writeInt(handler.objectID, true); + } + + public Object read (Kryo kryo, Input input, Class type) { + int objectID = input.readInt(true); + Connection connection = (Connection)kryo.getContext().get("connection"); + Object object = getRegisteredObject(connection, objectID); + if (WARN && object == null) warn("kryonet", "Unknown object ID " + objectID + " for connection: " + connection); + return object; + } + }); + } + + static class CachedMethod { + Method method; + Serializer[] serializers; + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/rmi/RemoteObject.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/RemoteObject.java similarity index 98% rename from kryonet/src/com/esotericsoftware/kryonet/rmi/RemoteObject.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/RemoteObject.java index 2256eb17..cec1aca0 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/rmi/RemoteObject.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/RemoteObject.java @@ -1,58 +1,58 @@ - -package com.esotericsoftware.kryonet.rmi; - -import com.esotericsoftware.kryonet.Connection; - -/** Provides access to various settings on a remote object. - * @see ObjectSpace#getRemoteObject(com.esotericsoftware.kryonet.Connection, int, Class...) - * @author Nathan Sweet */ -public interface RemoteObject { - /** Sets the milliseconds to wait for a method to return value. Default is 3000. */ - public void setResponseTimeout (int timeoutMillis); - - /** Sets the blocking behavior when invoking a remote method. Default is false. - * @param nonBlocking If false, the invoking thread will wait for the remote method to return or timeout (default). If true, - * the invoking thread will not wait for a response. The method will return immediately and the return value should - * be ignored. If they are being transmitted, the return value or any thrown exception can later be retrieved with - * {@link #waitForLastResponse()} or {@link #waitForResponse(byte)}. The responses will be stored until retrieved, so - * each method call should have a matching retrieve. */ - public void setNonBlocking (boolean nonBlocking); - - /** Sets whether return values are sent back when invoking a remote method. Default is true. - * @param transmit If true, then the return value for non-blocking method invocations can be retrieved with - * {@link #waitForLastResponse()} or {@link #waitForResponse(byte)}. If false, then non-primitive return values for - * remote method invocations are not sent by the remote side of the connection and the response can never be - * retrieved. This can also be used to save bandwidth if you will not check the return value of a blocking remote - * invocation. Note that an exception could still be returned by {@link #waitForLastResponse()} or - * {@link #waitForResponse(byte)} if {@link #setTransmitExceptions(boolean)} is true. */ - public void setTransmitReturnValue (boolean transmit); - - /** Sets whether exceptions are sent back when invoking a remote method. Default is true. - * @param transmit If false, exceptions will be unhandled and rethrown as RuntimeExceptions inside the invoking thread. This is - * the legacy behavior. If true, behavior is dependent on whether {@link #setNonBlocking(boolean)}. If non-blocking - * is true, the exception will be serialized and sent back to the call site of the remotely invoked method, where it - * will be re-thrown. If non-blocking is false, an exception will not be thrown in the calling thread but instead can - * be retrieved with {@link #waitForLastResponse()} or {@link #waitForResponse(byte)}, similar to a return value. */ - public void setTransmitExceptions (boolean transmit); - - /** Waits for the response to the last method invocation to be received or the response timeout to be reached. Must not be - * called from the connection's update thread. - * @see ObjectSpace#getRemoteObject(com.esotericsoftware.kryonet.Connection, int, Class...) */ - public Object waitForLastResponse (); - - /** Gets the ID of response for the last method invocation. */ - public byte getLastResponseID (); - - /** Waits for the specified method invocation response to be received or the response timeout to be reached. Must not be called - * from the connection's update thread. Response IDs use a six bit identifier, with one identifier reserved for "no response". - * This means that this method should be called to get the result for a non-blocking call before an additional 63 non-blocking - * calls are made, or risk undefined behavior due to identical IDs. - * @see ObjectSpace#getRemoteObject(com.esotericsoftware.kryonet.Connection, int, Class...) */ - public Object waitForResponse (byte responseID); - - /** Causes this RemoteObject to stop listening to the connection for method invocation response messages. */ - public void close (); - - /** Returns the local connection for this remote object. */ - public Connection getConnection (); -} + +package com.esotericsoftware.kryonet.rmi; + +import com.esotericsoftware.kryonet.Connection; + +/** Provides access to various settings on a remote object. + * @see ObjectSpace#getRemoteObject(com.esotericsoftware.kryonet.Connection, int, Class...) + * @author Nathan Sweet */ +public interface RemoteObject { + /** Sets the milliseconds to wait for a method to return value. Default is 3000. */ + public void setResponseTimeout (int timeoutMillis); + + /** Sets the blocking behavior when invoking a remote method. Default is false. + * @param nonBlocking If false, the invoking thread will wait for the remote method to return or timeout (default). If true, + * the invoking thread will not wait for a response. The method will return immediately and the return value should + * be ignored. If they are being transmitted, the return value or any thrown exception can later be retrieved with + * {@link #waitForLastResponse()} or {@link #waitForResponse(byte)}. The responses will be stored until retrieved, so + * each method call should have a matching retrieve. */ + public void setNonBlocking (boolean nonBlocking); + + /** Sets whether return values are sent back when invoking a remote method. Default is true. + * @param transmit If true, then the return value for non-blocking method invocations can be retrieved with + * {@link #waitForLastResponse()} or {@link #waitForResponse(byte)}. If false, then non-primitive return values for + * remote method invocations are not sent by the remote side of the connection and the response can never be + * retrieved. This can also be used to save bandwidth if you will not check the return value of a blocking remote + * invocation. Note that an exception could still be returned by {@link #waitForLastResponse()} or + * {@link #waitForResponse(byte)} if {@link #setTransmitExceptions(boolean)} is true. */ + public void setTransmitReturnValue (boolean transmit); + + /** Sets whether exceptions are sent back when invoking a remote method. Default is true. + * @param transmit If false, exceptions will be unhandled and rethrown as RuntimeExceptions inside the invoking thread. This is + * the legacy behavior. If true, behavior is dependent on whether {@link #setNonBlocking(boolean)}. If non-blocking + * is true, the exception will be serialized and sent back to the call site of the remotely invoked method, where it + * will be re-thrown. If non-blocking is false, an exception will not be thrown in the calling thread but instead can + * be retrieved with {@link #waitForLastResponse()} or {@link #waitForResponse(byte)}, similar to a return value. */ + public void setTransmitExceptions (boolean transmit); + + /** Waits for the response to the last method invocation to be received or the response timeout to be reached. Must not be + * called from the connection's update thread. + * @see ObjectSpace#getRemoteObject(com.esotericsoftware.kryonet.Connection, int, Class...) */ + public Object waitForLastResponse (); + + /** Gets the ID of response for the last method invocation. */ + public byte getLastResponseID (); + + /** Waits for the specified method invocation response to be received or the response timeout to be reached. Must not be called + * from the connection's update thread. Response IDs use a six bit identifier, with one identifier reserved for "no response". + * This means that this method should be called to get the result for a non-blocking call before an additional 63 non-blocking + * calls are made, or risk undefined behavior due to identical IDs. + * @see ObjectSpace#getRemoteObject(com.esotericsoftware.kryonet.Connection, int, Class...) */ + public Object waitForResponse (byte responseID); + + /** Causes this RemoteObject to stop listening to the connection for method invocation response messages. */ + public void close (); + + /** Returns the local connection for this remote object. */ + public Connection getConnection (); +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/rmi/TimeoutException.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/TimeoutException.java similarity index 96% rename from kryonet/src/com/esotericsoftware/kryonet/rmi/TimeoutException.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/TimeoutException.java index bc2a8456..ed74a2b7 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/rmi/TimeoutException.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/TimeoutException.java @@ -1,24 +1,24 @@ - -package com.esotericsoftware.kryonet.rmi; - -/** Thrown when a method with a return value is invoked on a remote object and the response is not received with the - * {@link RemoteObject#setResponseTimeout(int) response timeout}. - * @see ObjectSpace#getRemoteObject(com.esotericsoftware.kryonet.Connection, int, Class...) - * @author Nathan Sweet */ -public class TimeoutException extends RuntimeException { - public TimeoutException () { - super(); - } - - public TimeoutException (String message, Throwable cause) { - super(message, cause); - } - - public TimeoutException (String message) { - super(message); - } - - public TimeoutException (Throwable cause) { - super(cause); - } -} + +package com.esotericsoftware.kryonet.rmi; + +/** Thrown when a method with a return value is invoked on a remote object and the response is not received with the + * {@link RemoteObject#setResponseTimeout(int) response timeout}. + * @see ObjectSpace#getRemoteObject(com.esotericsoftware.kryonet.Connection, int, Class...) + * @author Nathan Sweet */ +public class TimeoutException extends RuntimeException { + public TimeoutException () { + super(); + } + + public TimeoutException (String message, Throwable cause) { + super(message, cause); + } + + public TimeoutException (String message) { + super(message); + } + + public TimeoutException (Throwable cause) { + super(cause); + } +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/util/InputStreamSender.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/util/InputStreamSender.java similarity index 95% rename from kryonet/src/com/esotericsoftware/kryonet/util/InputStreamSender.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/util/InputStreamSender.java index 057e2543..e74b45df 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/util/InputStreamSender.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/util/InputStreamSender.java @@ -1,38 +1,38 @@ - -package com.esotericsoftware.kryonet.util; - -import java.io.IOException; -import java.io.InputStream; - -import com.esotericsoftware.kryonet.KryoNetException; - -abstract public class InputStreamSender extends TcpIdleSender { - private final InputStream input; - private final byte[] chunk; - - public InputStreamSender (InputStream input, int chunkSize) { - this.input = input; - chunk = new byte[chunkSize]; - } - - protected final Object next () { - try { - int total = 0; - while (total < chunk.length) { - int count = input.read(chunk, total, chunk.length - total); - if (count < 0) { - if (total == 0) return null; - byte[] partial = new byte[total]; - System.arraycopy(chunk, 0, partial, 0, total); - return next(partial); - } - total += count; - } - } catch (IOException ex) { - throw new KryoNetException(ex); - } - return next(chunk); - } - - abstract protected Object next (byte[] chunk); -} + +package com.esotericsoftware.kryonet.util; + +import java.io.IOException; +import java.io.InputStream; + +import com.esotericsoftware.kryonet.KryoNetException; + +abstract public class InputStreamSender extends TcpIdleSender { + private final InputStream input; + private final byte[] chunk; + + public InputStreamSender (InputStream input, int chunkSize) { + this.input = input; + chunk = new byte[chunkSize]; + } + + protected final Object next () { + try { + int total = 0; + while (total < chunk.length) { + int count = input.read(chunk, total, chunk.length - total); + if (count < 0) { + if (total == 0) return null; + byte[] partial = new byte[total]; + System.arraycopy(chunk, 0, partial, 0, total); + return next(partial); + } + total += count; + } + } catch (IOException ex) { + throw new KryoNetException(ex); + } + return next(chunk); + } + + abstract protected Object next (byte[] chunk); +} diff --git a/kryonet/src/com/esotericsoftware/kryonet/util/TcpIdleSender.java b/kryonet/src/main/java/com/esotericsoftware/kryonet/util/TcpIdleSender.java similarity index 96% rename from kryonet/src/com/esotericsoftware/kryonet/util/TcpIdleSender.java rename to kryonet/src/main/java/com/esotericsoftware/kryonet/util/TcpIdleSender.java index 2e0896ea..024434f7 100644 --- a/kryonet/src/com/esotericsoftware/kryonet/util/TcpIdleSender.java +++ b/kryonet/src/main/java/com/esotericsoftware/kryonet/util/TcpIdleSender.java @@ -1,29 +1,29 @@ - -package com.esotericsoftware.kryonet.util; - -import com.esotericsoftware.kryonet.Connection; -import com.esotericsoftware.kryonet.Listener; - -abstract public class TcpIdleSender extends Listener { - boolean started; - - public void idle (Connection connection) { - if (!started) { - started = true; - start(); - } - Object object = next(); - if (object == null) - connection.removeListener(this); - else - connection.sendTCP(object); - } - - /** Called once, before the first send. Subclasses can override this method to send something so the receiving side expects - * subsequent objects. */ - protected void start () { - } - - /** Returns the next object to send, or null if no more objects will be sent. */ - abstract protected Object next (); -} + +package com.esotericsoftware.kryonet.util; + +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.Listener; + +abstract public class TcpIdleSender extends Listener { + boolean started; + + public void idle (Connection connection) { + if (!started) { + started = true; + start(); + } + Object object = next(); + if (object == null) + connection.removeListener(this); + else + connection.sendTCP(object); + } + + /** Called once, before the first send. Subclasses can override this method to send something so the receiving side expects + * subsequent objects. */ + protected void start () { + } + + /** Returns the next object to send, or null if no more objects will be sent. */ + abstract protected Object next (); +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/BufferTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/BufferTest.java similarity index 96% rename from kryonet/test/com/esotericsoftware/kryonet/BufferTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/BufferTest.java index 531c9d1e..597b1c53 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/BufferTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/BufferTest.java @@ -1,91 +1,91 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.util.concurrent.atomic.AtomicInteger; - -import com.esotericsoftware.kryo.Kryo; - -public class BufferTest extends KryoNetTestCase { - AtomicInteger received = new AtomicInteger(); - AtomicInteger receivedBytes = new AtomicInteger(); - - public void testManyLargeMessages () throws IOException { - final int messageCount = 1024; - int objectBufferSize = 10250; - int writeBufferSize = 10250 * messageCount; - - Server server = new Server(writeBufferSize, objectBufferSize); - startEndPoint(server); - register(server.getKryo()); - server.bind(tcpPort); - - server.addListener(new Listener() { - AtomicInteger received = new AtomicInteger(); - AtomicInteger receivedBytes = new AtomicInteger(); - - public void received (Connection connection, Object object) { - if (object instanceof LargeMessage) { - System.out.println("Server sending message: " + received.get()); - connection.sendTCP(object); - - receivedBytes.addAndGet(((LargeMessage)object).bytes.length); - - int count = received.incrementAndGet(); - System.out.println("Server received " + count + " messages."); - if (count == messageCount) { - System.out.println("Server received all " + messageCount + " messages!"); - System.out.println("Server received and sent " + receivedBytes.get() + " bytes."); - } - } - } - }); - - final Client client = new Client(writeBufferSize, objectBufferSize); - startEndPoint(client); - register(client.getKryo()); - client.connect(5000, host, tcpPort); - - client.addListener(new Listener() { - AtomicInteger received = new AtomicInteger(); - AtomicInteger receivedBytes = new AtomicInteger(); - - public void received (Connection connection, Object object) { - if (object instanceof LargeMessage) { - int count = received.incrementAndGet(); - System.out.println("Client received " + count + " messages."); - if (count == messageCount) { - System.out.println("Client received all " + messageCount + " messages!"); - System.out.println("Client received and sent " + receivedBytes.get() + " bytes."); - stopEndPoints(); - } - } - } - }); - - byte[] b = new byte[1024 * 10]; - for (int i = 0; i < messageCount; i++) { - System.out.println("Client sending: " + i); - client.sendTCP(new LargeMessage(b)); - } - System.out.println("Client has queued " + messageCount + " messages."); - - waitForThreads(5000); - } - - private void register (Kryo kryo) { - kryo.register(byte[].class); - kryo.register(LargeMessage.class); - } - - public static class LargeMessage { - public byte[] bytes; - - public LargeMessage () { - } - - public LargeMessage (byte[] bytes) { - this.bytes = bytes; - } - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +import com.esotericsoftware.kryo.Kryo; + +public class BufferTest extends KryoNetTestCase { + AtomicInteger received = new AtomicInteger(); + AtomicInteger receivedBytes = new AtomicInteger(); + + public void testManyLargeMessages () throws IOException { + final int messageCount = 1024; + int objectBufferSize = 10250; + int writeBufferSize = 10250 * messageCount; + + Server server = new Server(writeBufferSize, objectBufferSize); + startEndPoint(server); + register(server.getKryo()); + server.bind(tcpPort); + + server.addListener(new Listener() { + AtomicInteger received = new AtomicInteger(); + AtomicInteger receivedBytes = new AtomicInteger(); + + public void received (Connection connection, Object object) { + if (object instanceof LargeMessage) { + System.out.println("Server sending message: " + received.get()); + connection.sendTCP(object); + + receivedBytes.addAndGet(((LargeMessage)object).bytes.length); + + int count = received.incrementAndGet(); + System.out.println("Server received " + count + " messages."); + if (count == messageCount) { + System.out.println("Server received all " + messageCount + " messages!"); + System.out.println("Server received and sent " + receivedBytes.get() + " bytes."); + } + } + } + }); + + final Client client = new Client(writeBufferSize, objectBufferSize); + startEndPoint(client); + register(client.getKryo()); + client.connect(5000, host, tcpPort); + + client.addListener(new Listener() { + AtomicInteger received = new AtomicInteger(); + AtomicInteger receivedBytes = new AtomicInteger(); + + public void received (Connection connection, Object object) { + if (object instanceof LargeMessage) { + int count = received.incrementAndGet(); + System.out.println("Client received " + count + " messages."); + if (count == messageCount) { + System.out.println("Client received all " + messageCount + " messages!"); + System.out.println("Client received and sent " + receivedBytes.get() + " bytes."); + stopEndPoints(); + } + } + } + }); + + byte[] b = new byte[1024 * 10]; + for (int i = 0; i < messageCount; i++) { + System.out.println("Client sending: " + i); + client.sendTCP(new LargeMessage(b)); + } + System.out.println("Client has queued " + messageCount + " messages."); + + waitForThreads(5000); + } + + private void register (Kryo kryo) { + kryo.register(byte[].class); + kryo.register(LargeMessage.class); + } + + public static class LargeMessage { + public byte[] bytes; + + public LargeMessage () { + } + + public LargeMessage (byte[] bytes) { + this.bytes = bytes; + } + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/DiscoverHostTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/DiscoverHostTest.java similarity index 95% rename from kryonet/test/com/esotericsoftware/kryonet/DiscoverHostTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/DiscoverHostTest.java index 60956e96..f45e3520 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/DiscoverHostTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/DiscoverHostTest.java @@ -1,41 +1,41 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.net.InetAddress; - -public class DiscoverHostTest extends KryoNetTestCase { - public void testBroadcast () throws IOException { - // This server exists solely to reply to Client#discoverHost. - // It wouldn't be needed if the real server was using UDP. - final Server broadcastServer = new Server(); - startEndPoint(broadcastServer); - broadcastServer.bind(0, udpPort); - - final Server server = new Server(); - startEndPoint(server); - server.bind(54555); - server.addListener(new Listener() { - public void disconnected (Connection connection) { - broadcastServer.stop(); - server.stop(); - } - }); - - // ---- - - Client client = new Client(); - InetAddress host = client.discoverHost(udpPort, 2000); - if (host == null) { - stopEndPoints(); - fail("No servers found."); - return; - } - - startEndPoint(client); - client.connect(2000, host, tcpPort); - client.stop(); - - waitForThreads(); - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.net.InetAddress; + +public class DiscoverHostTest extends KryoNetTestCase { + public void testBroadcast () throws IOException { + // This server exists solely to reply to Client#discoverHost. + // It wouldn't be needed if the real server was using UDP. + final Server broadcastServer = new Server(); + startEndPoint(broadcastServer); + broadcastServer.bind(0, udpPort); + + final Server server = new Server(); + startEndPoint(server); + server.bind(54555); + server.addListener(new Listener() { + public void disconnected (Connection connection) { + broadcastServer.stop(); + server.stop(); + } + }); + + // ---- + + Client client = new Client(); + InetAddress host = client.discoverHost(udpPort, 2000); + if (host == null) { + stopEndPoints(); + fail("No servers found."); + return; + } + + startEndPoint(client); + client.connect(2000, host, tcpPort); + client.stop(); + + waitForThreads(); + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/InputStreamSenderTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/InputStreamSenderTest.java similarity index 96% rename from kryonet/test/com/esotericsoftware/kryonet/InputStreamSenderTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/InputStreamSenderTest.java index 4a3cde1b..3ca345f3 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/InputStreamSenderTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/InputStreamSenderTest.java @@ -1,67 +1,67 @@ - -package com.esotericsoftware.kryonet; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import com.esotericsoftware.kryonet.util.InputStreamSender; - -public class InputStreamSenderTest extends KryoNetTestCase { - boolean success; - - public void testStream () throws IOException { - final int largeDataSize = 12345; - - final Server server = new Server(16384, 8192); - server.getKryo().setRegistrationRequired(false); - startEndPoint(server); - server.bind(tcpPort, udpPort); - server.addListener(new Listener() { - public void connected (Connection connection) { - ByteArrayOutputStream output = new ByteArrayOutputStream(largeDataSize); - for (int i = 0; i < largeDataSize; i++) - output.write(i); - ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); - // Send data in 512 byte chunks. - connection.addListener(new InputStreamSender(input, 512) { - protected void start () { - // Normally would send an object so the receiving side knows how to handle the chunks we are about to send. - System.out.println("starting"); - } - - protected Object next (byte[] bytes) { - System.out.println("sending " + bytes.length); - return bytes; // Normally would wrap the byte[] with an object so the receiving side knows how to handle it. - } - }); - } - }); - - // ---- - - final Client client = new Client(16384, 8192); - client.getKryo().setRegistrationRequired(false); - startEndPoint(client); - client.addListener(new Listener() { - int total; - - public void received (Connection connection, Object object) { - if (object instanceof byte[]) { - int length = ((byte[])object).length; - System.out.println("received " + length); - total += length; - if (total == largeDataSize) { - success = true; - stopEndPoints(); - } - } - } - }); - - client.connect(5000, host, tcpPort, udpPort); - - waitForThreads(5000); - if (!success) fail(); - } -} + +package com.esotericsoftware.kryonet; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import com.esotericsoftware.kryonet.util.InputStreamSender; + +public class InputStreamSenderTest extends KryoNetTestCase { + boolean success; + + public void testStream () throws IOException { + final int largeDataSize = 12345; + + final Server server = new Server(16384, 8192); + server.getKryo().setRegistrationRequired(false); + startEndPoint(server); + server.bind(tcpPort, udpPort); + server.addListener(new Listener() { + public void connected (Connection connection) { + ByteArrayOutputStream output = new ByteArrayOutputStream(largeDataSize); + for (int i = 0; i < largeDataSize; i++) + output.write(i); + ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); + // Send data in 512 byte chunks. + connection.addListener(new InputStreamSender(input, 512) { + protected void start () { + // Normally would send an object so the receiving side knows how to handle the chunks we are about to send. + System.out.println("starting"); + } + + protected Object next (byte[] bytes) { + System.out.println("sending " + bytes.length); + return bytes; // Normally would wrap the byte[] with an object so the receiving side knows how to handle it. + } + }); + } + }); + + // ---- + + final Client client = new Client(16384, 8192); + client.getKryo().setRegistrationRequired(false); + startEndPoint(client); + client.addListener(new Listener() { + int total; + + public void received (Connection connection, Object object) { + if (object instanceof byte[]) { + int length = ((byte[])object).length; + System.out.println("received " + length); + total += length; + if (total == largeDataSize) { + success = true; + stopEndPoints(); + } + } + } + }); + + client.connect(5000, host, tcpPort, udpPort); + + waitForThreads(5000); + if (!success) fail(); + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/JsonTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/JsonTest.java similarity index 97% rename from kryonet/test/com/esotericsoftware/kryonet/JsonTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/JsonTest.java index a662a419..ca2c04b8 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/JsonTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/JsonTest.java @@ -1,165 +1,165 @@ - -package com.esotericsoftware.kryonet; - -import com.esotericsoftware.jsonbeans.JsonWriter; - -import java.io.IOException; -import java.io.StringWriter; -import java.util.Arrays; - -public class JsonTest extends KryoNetTestCase { - String fail; - - public void testJson () throws IOException { - fail = null; - - final Data dataTCP = new Data(); - populateData(dataTCP, true); - final Data dataUDP = new Data(); - populateData(dataUDP, false); - - final Server server = new Server(16384, 8192, new JsonSerialization()); - startEndPoint(server); - server.bind(tcpPort, udpPort); - server.addListener(new Listener() { - public void connected (Connection connection) { - connection.sendTCP(dataTCP); - connection.sendUDP(dataUDP); // Note UDP ping pong stops if a UDP packet is lost. - } - - public void received (Connection connection, Object object) { - if (object instanceof Data) { - Data data = (Data)object; - if (data.isTCP) { - if (!data.equals(dataTCP)) { - fail = "TCP data is not equal on server."; - throw new RuntimeException("Fail!"); - } - connection.sendTCP(data); - } else { - if (!data.equals(dataUDP)) { - fail = "UDP data is not equal on server."; - throw new RuntimeException("Fail!"); - } - connection.sendUDP(data); - } - } - } - }); - - // ---- - - final Client client = new Client(16384, 8192, new JsonSerialization()); - startEndPoint(client); - client.addListener(new Listener() { - public void received (Connection connection, Object object) { - if (object instanceof Data) { - Data data = (Data)object; - if (data.isTCP) { - if (!data.equals(dataTCP)) { - fail = "TCP data is not equal on client."; - throw new RuntimeException("Fail!"); - } - connection.sendTCP(data); - } else { - if (!data.equals(dataUDP)) { - fail = "UDP data is not equal on client."; - throw new RuntimeException("Fail!"); - } - connection.sendUDP(data); - } - } - } - }); - - client.connect(5000, host, tcpPort, udpPort); - - waitForThreads(5000); - - if (fail != null) fail(fail); - } - - private void populateData (Data data, boolean isTCP) { - data.isTCP = isTCP; - - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < 3000; i++) - buffer.append('a'); - data.string = buffer.toString(); - - data.strings = new String[] {"abcdefghijklmnopqrstuvwxyz0123456789", "", null, "!@#$", "�����"}; - data.ints = new int[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; - data.shorts = new short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; - data.floats = new float[] {0, 1, -1, 123456, -123456, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, Float.MIN_VALUE}; - data.bytes = new byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; - data.booleans = new boolean[] {true, false}; - data.Ints = new Integer[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; - data.Shorts = new Short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; - data.Floats = new Float[] {0f, 1f, -1f, 123456f, -123456f, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, - Float.MIN_VALUE}; - data.Bytes = new Byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; - data.Booleans = new Boolean[] {true, false}; - } - - static public class Data { - public String string; - public String[] strings; - public int[] ints; - public short[] shorts; - public float[] floats; - public byte[] bytes; - public boolean[] booleans; - public Integer[] Ints; - public Short[] Shorts; - public Float[] Floats; - public Byte[] Bytes; - public Boolean[] Booleans; - public boolean isTCP; - - public int hashCode () { - final int prime = 31; - int result = 1; - result = prime * result + Arrays.hashCode(Booleans); - result = prime * result + Arrays.hashCode(Bytes); - result = prime * result + Arrays.hashCode(Floats); - result = prime * result + Arrays.hashCode(Ints); - result = prime * result + Arrays.hashCode(Shorts); - result = prime * result + Arrays.hashCode(booleans); - result = prime * result + Arrays.hashCode(bytes); - result = prime * result + Arrays.hashCode(floats); - result = prime * result + Arrays.hashCode(ints); - result = prime * result + (isTCP ? 1231 : 1237); - result = prime * result + Arrays.hashCode(shorts); - result = prime * result + ((string == null) ? 0 : string.hashCode()); - result = prime * result + Arrays.hashCode(strings); - return result; - } - - public boolean equals (Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - Data other = (Data)obj; - if (!Arrays.equals(Booleans, other.Booleans)) return false; - if (!Arrays.equals(Bytes, other.Bytes)) return false; - if (!Arrays.equals(Floats, other.Floats)) return false; - if (!Arrays.equals(Ints, other.Ints)) return false; - if (!Arrays.equals(Shorts, other.Shorts)) return false; - if (!Arrays.equals(booleans, other.booleans)) return false; - if (!Arrays.equals(bytes, other.bytes)) return false; - if (!Arrays.equals(floats, other.floats)) return false; - if (!Arrays.equals(ints, other.ints)) return false; - if (isTCP != other.isTCP) return false; - if (!Arrays.equals(shorts, other.shorts)) return false; - if (string == null) { - if (other.string != null) return false; - } else if (!string.equals(other.string)) return false; - if (!Arrays.equals(strings, other.strings)) return false; - return true; - } - - public String toString () { - return "Data"; - } - } -} + +package com.esotericsoftware.kryonet; + +import com.esotericsoftware.jsonbeans.JsonWriter; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.Arrays; + +public class JsonTest extends KryoNetTestCase { + String fail; + + public void testJson () throws IOException { + fail = null; + + final Data dataTCP = new Data(); + populateData(dataTCP, true); + final Data dataUDP = new Data(); + populateData(dataUDP, false); + + final Server server = new Server(16384, 8192, new JsonSerialization()); + startEndPoint(server); + server.bind(tcpPort, udpPort); + server.addListener(new Listener() { + public void connected (Connection connection) { + connection.sendTCP(dataTCP); + connection.sendUDP(dataUDP); // Note UDP ping pong stops if a UDP packet is lost. + } + + public void received (Connection connection, Object object) { + if (object instanceof Data) { + Data data = (Data)object; + if (data.isTCP) { + if (!data.equals(dataTCP)) { + fail = "TCP data is not equal on server."; + throw new RuntimeException("Fail!"); + } + connection.sendTCP(data); + } else { + if (!data.equals(dataUDP)) { + fail = "UDP data is not equal on server."; + throw new RuntimeException("Fail!"); + } + connection.sendUDP(data); + } + } + } + }); + + // ---- + + final Client client = new Client(16384, 8192, new JsonSerialization()); + startEndPoint(client); + client.addListener(new Listener() { + public void received (Connection connection, Object object) { + if (object instanceof Data) { + Data data = (Data)object; + if (data.isTCP) { + if (!data.equals(dataTCP)) { + fail = "TCP data is not equal on client."; + throw new RuntimeException("Fail!"); + } + connection.sendTCP(data); + } else { + if (!data.equals(dataUDP)) { + fail = "UDP data is not equal on client."; + throw new RuntimeException("Fail!"); + } + connection.sendUDP(data); + } + } + } + }); + + client.connect(5000, host, tcpPort, udpPort); + + waitForThreads(5000); + + if (fail != null) fail(fail); + } + + private void populateData (Data data, boolean isTCP) { + data.isTCP = isTCP; + + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < 3000; i++) + buffer.append('a'); + data.string = buffer.toString(); + + data.strings = new String[] {"abcdefghijklmnopqrstuvwxyz0123456789", "", null, "!@#$", "�����"}; + data.ints = new int[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; + data.shorts = new short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; + data.floats = new float[] {0, 1, -1, 123456, -123456, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, Float.MIN_VALUE}; + data.bytes = new byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; + data.booleans = new boolean[] {true, false}; + data.Ints = new Integer[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; + data.Shorts = new Short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; + data.Floats = new Float[] {0f, 1f, -1f, 123456f, -123456f, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, + Float.MIN_VALUE}; + data.Bytes = new Byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; + data.Booleans = new Boolean[] {true, false}; + } + + static public class Data { + public String string; + public String[] strings; + public int[] ints; + public short[] shorts; + public float[] floats; + public byte[] bytes; + public boolean[] booleans; + public Integer[] Ints; + public Short[] Shorts; + public Float[] Floats; + public Byte[] Bytes; + public Boolean[] Booleans; + public boolean isTCP; + + public int hashCode () { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(Booleans); + result = prime * result + Arrays.hashCode(Bytes); + result = prime * result + Arrays.hashCode(Floats); + result = prime * result + Arrays.hashCode(Ints); + result = prime * result + Arrays.hashCode(Shorts); + result = prime * result + Arrays.hashCode(booleans); + result = prime * result + Arrays.hashCode(bytes); + result = prime * result + Arrays.hashCode(floats); + result = prime * result + Arrays.hashCode(ints); + result = prime * result + (isTCP ? 1231 : 1237); + result = prime * result + Arrays.hashCode(shorts); + result = prime * result + ((string == null) ? 0 : string.hashCode()); + result = prime * result + Arrays.hashCode(strings); + return result; + } + + public boolean equals (Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Data other = (Data)obj; + if (!Arrays.equals(Booleans, other.Booleans)) return false; + if (!Arrays.equals(Bytes, other.Bytes)) return false; + if (!Arrays.equals(Floats, other.Floats)) return false; + if (!Arrays.equals(Ints, other.Ints)) return false; + if (!Arrays.equals(Shorts, other.Shorts)) return false; + if (!Arrays.equals(booleans, other.booleans)) return false; + if (!Arrays.equals(bytes, other.bytes)) return false; + if (!Arrays.equals(floats, other.floats)) return false; + if (!Arrays.equals(ints, other.ints)) return false; + if (isTCP != other.isTCP) return false; + if (!Arrays.equals(shorts, other.shorts)) return false; + if (string == null) { + if (other.string != null) return false; + } else if (!string.equals(other.string)) return false; + if (!Arrays.equals(strings, other.strings)) return false; + return true; + } + + public String toString () { + return "Data"; + } + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/KryoNetTestCase.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/KryoNetTestCase.java similarity index 96% rename from kryonet/test/com/esotericsoftware/kryonet/KryoNetTestCase.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/KryoNetTestCase.java index 7944e867..40cfb72f 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/KryoNetTestCase.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/KryoNetTestCase.java @@ -1,91 +1,91 @@ - -package com.esotericsoftware.kryonet; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Timer; -import java.util.TimerTask; - -import junit.framework.TestCase; - -import com.esotericsoftware.minlog.Log; - -abstract public class KryoNetTestCase extends TestCase { - static public String host = "localhost"; - static public int tcpPort = 54555, udpPort = 54777; - - private ArrayList threads = new ArrayList(); - ArrayList endPoints = new ArrayList(); - private Timer timer; - boolean fail; - - public KryoNetTestCase () { - // Log.TRACE(); - Log.DEBUG(); - } - - protected void setUp () throws Exception { - System.out.println("---- " + getClass().getSimpleName()); - timer = new Timer(); - } - - protected void tearDown () throws Exception { - timer.cancel(); - } - - public void startEndPoint (EndPoint endPoint) { - endPoints.add(endPoint); - Thread thread = new Thread(endPoint, endPoint.getClass().getSimpleName()); - threads.add(thread); - thread.start(); - } - - public void stopEndPoints () { - stopEndPoints(0); - } - - public void stopEndPoints (int stopAfterMillis) { - timer.schedule(new TimerTask() { - public void run () { - for (EndPoint endPoint : endPoints) - endPoint.stop(); - endPoints.clear(); - } - }, stopAfterMillis); - } - - public void waitForThreads (int stopAfterMillis) { - if (stopAfterMillis > 10000) throw new IllegalArgumentException("stopAfterMillis must be < 10000"); - stopEndPoints(stopAfterMillis); - waitForThreads(); - } - - public void waitForThreads () { - fail = false; - TimerTask failTask = new TimerTask() { - public void run () { - stopEndPoints(); - fail = true; - } - }; - timer.schedule(failTask, 11000); - while (true) { - for (Iterator iter = threads.iterator(); iter.hasNext();) { - Thread thread = (Thread)iter.next(); - if (!thread.isAlive()) iter.remove(); - } - if (threads.isEmpty()) break; - try { - Thread.sleep(100); - } catch (InterruptedException ignored) { - } - } - failTask.cancel(); - if (fail) fail("Test did not complete in a timely manner."); - // Give sockets a chance to close before starting the next test. - try { - Thread.sleep(1000); - } catch (InterruptedException ignored) { - } - } -} + +package com.esotericsoftware.kryonet; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Timer; +import java.util.TimerTask; + +import junit.framework.TestCase; + +import com.esotericsoftware.minlog.Log; + +abstract public class KryoNetTestCase extends TestCase { + static public String host = "localhost"; + static public int tcpPort = 54555, udpPort = 54777; + + private ArrayList threads = new ArrayList(); + ArrayList endPoints = new ArrayList(); + private Timer timer; + boolean fail; + + public KryoNetTestCase () { + // Log.TRACE(); + Log.DEBUG(); + } + + protected void setUp () throws Exception { + System.out.println("---- " + getClass().getSimpleName()); + timer = new Timer(); + } + + protected void tearDown () throws Exception { + timer.cancel(); + } + + public void startEndPoint (EndPoint endPoint) { + endPoints.add(endPoint); + Thread thread = new Thread(endPoint, endPoint.getClass().getSimpleName()); + threads.add(thread); + thread.start(); + } + + public void stopEndPoints () { + stopEndPoints(0); + } + + public void stopEndPoints (int stopAfterMillis) { + timer.schedule(new TimerTask() { + public void run () { + for (EndPoint endPoint : endPoints) + endPoint.stop(); + endPoints.clear(); + } + }, stopAfterMillis); + } + + public void waitForThreads (int stopAfterMillis) { + if (stopAfterMillis > 10000) throw new IllegalArgumentException("stopAfterMillis must be < 10000"); + stopEndPoints(stopAfterMillis); + waitForThreads(); + } + + public void waitForThreads () { + fail = false; + TimerTask failTask = new TimerTask() { + public void run () { + stopEndPoints(); + fail = true; + } + }; + timer.schedule(failTask, 11000); + while (true) { + for (Iterator iter = threads.iterator(); iter.hasNext();) { + Thread thread = (Thread)iter.next(); + if (!thread.isAlive()) iter.remove(); + } + if (threads.isEmpty()) break; + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + } + failTask.cancel(); + if (fail) fail("Test did not complete in a timely manner."); + // Give sockets a chance to close before starting the next test. + try { + Thread.sleep(1000); + } catch (InterruptedException ignored) { + } + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/MultipleServerTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/MultipleServerTest.java similarity index 96% rename from kryonet/test/com/esotericsoftware/kryonet/MultipleServerTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/MultipleServerTest.java index a6cbb7ab..2769930c 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/MultipleServerTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/MultipleServerTest.java @@ -1,64 +1,64 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.util.Arrays; -import java.util.concurrent.atomic.AtomicInteger; - -import com.esotericsoftware.kryo.Kryo; - -public class MultipleServerTest extends KryoNetTestCase { - AtomicInteger received = new AtomicInteger(); - - public void testMultipleThreads () throws IOException { - final Server server1 = new Server(16384, 8192); - server1.getKryo().register(String[].class); - startEndPoint(server1); - server1.bind(tcpPort, udpPort); - server1.addListener(new Listener() { - public void received (Connection connection, Object object) { - if (object instanceof String) { - if (!object.equals("client1")) fail(); - if (received.incrementAndGet() == 2) stopEndPoints(); - } - } - }); - - final Server server2 = new Server(16384, 8192); - server2.getKryo().register(String[].class); - startEndPoint(server2); - server2.bind(tcpPort + 1, udpPort + 1); - server2.addListener(new Listener() { - public void received (Connection connection, Object object) { - if (object instanceof String) { - if (!object.equals("client2")) fail(); - if (received.incrementAndGet() == 2) stopEndPoints(); - } - } - }); - - // ---- - - Client client1 = new Client(16384, 8192); - client1.getKryo().register(String[].class); - startEndPoint(client1); - client1.addListener(new Listener() { - public void connected (Connection connection) { - connection.sendTCP("client1"); - } - }); - client1.connect(5000, host, tcpPort, udpPort); - - Client client2 = new Client(16384, 8192); - client2.getKryo().register(String[].class); - startEndPoint(client2); - client2.addListener(new Listener() { - public void connected (Connection connection) { - connection.sendTCP("client2"); - } - }); - client2.connect(5000, host, tcpPort + 1, udpPort + 1); - - waitForThreads(5000); - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +import com.esotericsoftware.kryo.Kryo; + +public class MultipleServerTest extends KryoNetTestCase { + AtomicInteger received = new AtomicInteger(); + + public void testMultipleThreads () throws IOException { + final Server server1 = new Server(16384, 8192); + server1.getKryo().register(String[].class); + startEndPoint(server1); + server1.bind(tcpPort, udpPort); + server1.addListener(new Listener() { + public void received (Connection connection, Object object) { + if (object instanceof String) { + if (!object.equals("client1")) fail(); + if (received.incrementAndGet() == 2) stopEndPoints(); + } + } + }); + + final Server server2 = new Server(16384, 8192); + server2.getKryo().register(String[].class); + startEndPoint(server2); + server2.bind(tcpPort + 1, udpPort + 1); + server2.addListener(new Listener() { + public void received (Connection connection, Object object) { + if (object instanceof String) { + if (!object.equals("client2")) fail(); + if (received.incrementAndGet() == 2) stopEndPoints(); + } + } + }); + + // ---- + + Client client1 = new Client(16384, 8192); + client1.getKryo().register(String[].class); + startEndPoint(client1); + client1.addListener(new Listener() { + public void connected (Connection connection) { + connection.sendTCP("client1"); + } + }); + client1.connect(5000, host, tcpPort, udpPort); + + Client client2 = new Client(16384, 8192); + client2.getKryo().register(String[].class); + startEndPoint(client2); + client2.addListener(new Listener() { + public void connected (Connection connection) { + connection.sendTCP("client2"); + } + }); + client2.connect(5000, host, tcpPort + 1, udpPort + 1); + + waitForThreads(5000); + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/MultipleThreadTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/MultipleThreadTest.java similarity index 96% rename from kryonet/test/com/esotericsoftware/kryonet/MultipleThreadTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/MultipleThreadTest.java index 9cc1cb9c..9acc0c61 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/MultipleThreadTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/MultipleThreadTest.java @@ -1,78 +1,78 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.util.Arrays; - -import com.esotericsoftware.kryo.Kryo; - -public class MultipleThreadTest extends KryoNetTestCase { - int receivedServer, receivedClient1, receivedClient2; - - public void testMultipleThreads () throws IOException { - receivedServer = 0; - - final int messageCount = 10; - final int threads = 5; - final int sleepMillis = 50; - final int clients = 3; - - final Server server = new Server(16384, 8192); - server.getKryo().register(String[].class); - startEndPoint(server); - server.bind(tcpPort, udpPort); - server.addListener(new Listener() { - public void received (Connection connection, Object object) { - receivedServer++; - if (receivedServer == messageCount * clients) stopEndPoints(); - } - }); - - // ---- - - for (int i = 0; i < clients; i++) { - Client client = new Client(16384, 8192); - client.getKryo().register(String[].class); - startEndPoint(client); - client.addListener(new Listener() { - int received; - - public void received (Connection connection, Object object) { - if (object instanceof String) { - received++; - if (received == messageCount * threads) { - for (int i = 0; i < messageCount; i++) { - connection.sendTCP("message" + i); - try { - Thread.sleep(50); - } catch (InterruptedException ignored) { - } - } - } - } - } - }); - client.connect(5000, host, tcpPort, udpPort); - } - - for (int i = 0; i < threads; i++) { - new Thread() { - public void run () { - Connection[] connections = server.getConnections(); - for (int i = 0; i < messageCount; i++) { - for (int ii = 0, n = connections.length; ii < n; ii++) - connections[ii].sendTCP("message" + i); - try { - Thread.sleep(sleepMillis); - } catch (InterruptedException ignored) { - } - } - } - }.start(); - } - - waitForThreads(5000); - - assertEquals(messageCount * clients, receivedServer); - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.util.Arrays; + +import com.esotericsoftware.kryo.Kryo; + +public class MultipleThreadTest extends KryoNetTestCase { + int receivedServer, receivedClient1, receivedClient2; + + public void testMultipleThreads () throws IOException { + receivedServer = 0; + + final int messageCount = 10; + final int threads = 5; + final int sleepMillis = 50; + final int clients = 3; + + final Server server = new Server(16384, 8192); + server.getKryo().register(String[].class); + startEndPoint(server); + server.bind(tcpPort, udpPort); + server.addListener(new Listener() { + public void received (Connection connection, Object object) { + receivedServer++; + if (receivedServer == messageCount * clients) stopEndPoints(); + } + }); + + // ---- + + for (int i = 0; i < clients; i++) { + Client client = new Client(16384, 8192); + client.getKryo().register(String[].class); + startEndPoint(client); + client.addListener(new Listener() { + int received; + + public void received (Connection connection, Object object) { + if (object instanceof String) { + received++; + if (received == messageCount * threads) { + for (int i = 0; i < messageCount; i++) { + connection.sendTCP("message" + i); + try { + Thread.sleep(50); + } catch (InterruptedException ignored) { + } + } + } + } + } + }); + client.connect(5000, host, tcpPort, udpPort); + } + + for (int i = 0; i < threads; i++) { + new Thread() { + public void run () { + Connection[] connections = server.getConnections(); + for (int i = 0; i < messageCount; i++) { + for (int ii = 0, n = connections.length; ii < n; ii++) + connections[ii].sendTCP("message" + i); + try { + Thread.sleep(sleepMillis); + } catch (InterruptedException ignored) { + } + } + } + }.start(); + } + + waitForThreads(5000); + + assertEquals(messageCount * clients, receivedServer); + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/PingPongTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/PingPongTest.java similarity index 97% rename from kryonet/test/com/esotericsoftware/kryonet/PingPongTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/PingPongTest.java index 1aed38aa..307b4172 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/PingPongTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/PingPongTest.java @@ -1,211 +1,211 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.util.Arrays; - -import com.esotericsoftware.kryo.Kryo; - -public class PingPongTest extends KryoNetTestCase { - String fail; - - public void testPingPong () throws IOException { - fail = null; - - final Data dataTCP = new Data(); - populateData(dataTCP, true); - final Data dataUDP = new Data(); - populateData(dataUDP, false); - - final Server server = new Server(16384, 8192); - register(server.getKryo()); - startEndPoint(server); - server.bind(tcpPort, udpPort); - server.addListener(new Listener() { - public void connected (Connection connection) { - connection.sendTCP(dataTCP); - connection.sendUDP(dataUDP); // Note UDP ping pong stops if a UDP packet is lost. - } - - public void received (Connection connection, Object object) { - if (object instanceof Data) { - Data data = (Data)object; - if (data.isTCP) { - if (!data.equals(dataTCP)) { - fail = "TCP data is not equal on server."; - throw new RuntimeException("Fail!"); - } - connection.sendTCP(data); - } else { - if (!data.equals(dataUDP)) { - fail = "UDP data is not equal on server."; - throw new RuntimeException("Fail!"); - } - connection.sendUDP(data); - } - } - } - }); - - // ---- - - final Client client = new Client(16384, 8192); - register(client.getKryo()); - startEndPoint(client); - client.addListener(new Listener() { - public void received (Connection connection, Object object) { - if (object instanceof Data) { - Data data = (Data)object; - if (data.isTCP) { - if (!data.equals(dataTCP)) { - fail = "TCP data is not equal on client."; - throw new RuntimeException("Fail!"); - } - connection.sendTCP(data); - } else { - if (!data.equals(dataUDP)) { - fail = "UDP data is not equal on client."; - throw new RuntimeException("Fail!"); - } - connection.sendUDP(data); - } - } - } - }); - - client.connect(5000, host, tcpPort, udpPort); - - waitForThreads(5000); - } - - private void populateData (Data data, boolean isTCP) { - data.isTCP = isTCP; - - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < 3000; i++) - buffer.append('a'); - data.string = buffer.toString(); - - data.strings = new String[] {"abcdefghijklmnopqrstuvwxyz0123456789", "", null, "!@#$", "�����"}; - data.ints = new int[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; - data.shorts = new short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; - data.floats = new float[] {0, -0, 1, -1, 123456, -123456, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, - Float.MIN_VALUE}; - data.doubles = new double[] {0, -0, 1, -1, 123456, -123456, 0.1d, 0.2d, -0.3d, Math.PI, Double.MAX_VALUE, Double.MIN_VALUE}; - data.longs = new long[] {0, -0, 1, -1, 123456, -123456, 99999999999l, -99999999999l, Long.MAX_VALUE, Long.MIN_VALUE}; - data.bytes = new byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; - data.chars = new char[] {32345, 12345, 0, 1, 63, Character.MAX_VALUE, Character.MIN_VALUE}; - data.booleans = new boolean[] {true, false}; - data.Ints = new Integer[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; - data.Shorts = new Short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; - data.Floats = new Float[] {0f, -0f, 1f, -1f, 123456f, -123456f, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, - Float.MIN_VALUE}; - data.Doubles = new Double[] {0d, -0d, 1d, -1d, 123456d, -123456d, 0.1d, 0.2d, -0.3d, Math.PI, Double.MAX_VALUE, - Double.MIN_VALUE}; - data.Longs = new Long[] {0l, -0l, 1l, -1l, 123456l, -123456l, 99999999999l, -99999999999l, Long.MAX_VALUE, Long.MIN_VALUE}; - data.Bytes = new Byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; - data.Chars = new Character[] {32345, 12345, 0, 1, 63, Character.MAX_VALUE, Character.MIN_VALUE}; - data.Booleans = new Boolean[] {true, false}; - } - - private void register (Kryo kryo) { - kryo.register(String[].class); - kryo.register(int[].class); - kryo.register(short[].class); - kryo.register(float[].class); - kryo.register(double[].class); - kryo.register(long[].class); - kryo.register(byte[].class); - kryo.register(char[].class); - kryo.register(boolean[].class); - kryo.register(Integer[].class); - kryo.register(Short[].class); - kryo.register(Float[].class); - kryo.register(Double[].class); - kryo.register(Long[].class); - kryo.register(Byte[].class); - kryo.register(Character[].class); - kryo.register(Boolean[].class); - kryo.register(Data.class); - } - - static public class Data { - public String string; - public String[] strings; - public int[] ints; - public short[] shorts; - public float[] floats; - public double[] doubles; - public long[] longs; - public byte[] bytes; - public char[] chars; - public boolean[] booleans; - public Integer[] Ints; - public Short[] Shorts; - public Float[] Floats; - public Double[] Doubles; - public Long[] Longs; - public Byte[] Bytes; - public Character[] Chars; - public Boolean[] Booleans; - public boolean isTCP; - - public int hashCode () { - final int prime = 31; - int result = 1; - result = prime * result + Arrays.hashCode(Booleans); - result = prime * result + Arrays.hashCode(Bytes); - result = prime * result + Arrays.hashCode(Chars); - result = prime * result + Arrays.hashCode(Doubles); - result = prime * result + Arrays.hashCode(Floats); - result = prime * result + Arrays.hashCode(Ints); - result = prime * result + Arrays.hashCode(Longs); - result = prime * result + Arrays.hashCode(Shorts); - result = prime * result + Arrays.hashCode(booleans); - result = prime * result + Arrays.hashCode(bytes); - result = prime * result + Arrays.hashCode(chars); - result = prime * result + Arrays.hashCode(doubles); - result = prime * result + Arrays.hashCode(floats); - result = prime * result + Arrays.hashCode(ints); - result = prime * result + (isTCP ? 1231 : 1237); - result = prime * result + Arrays.hashCode(longs); - result = prime * result + Arrays.hashCode(shorts); - result = prime * result + ((string == null) ? 0 : string.hashCode()); - result = prime * result + Arrays.hashCode(strings); - return result; - } - - public boolean equals (Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - Data other = (Data)obj; - if (!Arrays.equals(Booleans, other.Booleans)) return false; - if (!Arrays.equals(Bytes, other.Bytes)) return false; - if (!Arrays.equals(Chars, other.Chars)) return false; - if (!Arrays.equals(Doubles, other.Doubles)) return false; - if (!Arrays.equals(Floats, other.Floats)) return false; - if (!Arrays.equals(Ints, other.Ints)) return false; - if (!Arrays.equals(Longs, other.Longs)) return false; - if (!Arrays.equals(Shorts, other.Shorts)) return false; - if (!Arrays.equals(booleans, other.booleans)) return false; - if (!Arrays.equals(bytes, other.bytes)) return false; - if (!Arrays.equals(chars, other.chars)) return false; - if (!Arrays.equals(doubles, other.doubles)) return false; - if (!Arrays.equals(floats, other.floats)) return false; - if (!Arrays.equals(ints, other.ints)) return false; - if (isTCP != other.isTCP) return false; - if (!Arrays.equals(longs, other.longs)) return false; - if (!Arrays.equals(shorts, other.shorts)) return false; - if (string == null) { - if (other.string != null) return false; - } else if (!string.equals(other.string)) return false; - if (!Arrays.equals(strings, other.strings)) return false; - return true; - } - - public String toString () { - return "Data"; - } - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.util.Arrays; + +import com.esotericsoftware.kryo.Kryo; + +public class PingPongTest extends KryoNetTestCase { + String fail; + + public void testPingPong () throws IOException { + fail = null; + + final Data dataTCP = new Data(); + populateData(dataTCP, true); + final Data dataUDP = new Data(); + populateData(dataUDP, false); + + final Server server = new Server(16384, 8192); + register(server.getKryo()); + startEndPoint(server); + server.bind(tcpPort, udpPort); + server.addListener(new Listener() { + public void connected (Connection connection) { + connection.sendTCP(dataTCP); + connection.sendUDP(dataUDP); // Note UDP ping pong stops if a UDP packet is lost. + } + + public void received (Connection connection, Object object) { + if (object instanceof Data) { + Data data = (Data)object; + if (data.isTCP) { + if (!data.equals(dataTCP)) { + fail = "TCP data is not equal on server."; + throw new RuntimeException("Fail!"); + } + connection.sendTCP(data); + } else { + if (!data.equals(dataUDP)) { + fail = "UDP data is not equal on server."; + throw new RuntimeException("Fail!"); + } + connection.sendUDP(data); + } + } + } + }); + + // ---- + + final Client client = new Client(16384, 8192); + register(client.getKryo()); + startEndPoint(client); + client.addListener(new Listener() { + public void received (Connection connection, Object object) { + if (object instanceof Data) { + Data data = (Data)object; + if (data.isTCP) { + if (!data.equals(dataTCP)) { + fail = "TCP data is not equal on client."; + throw new RuntimeException("Fail!"); + } + connection.sendTCP(data); + } else { + if (!data.equals(dataUDP)) { + fail = "UDP data is not equal on client."; + throw new RuntimeException("Fail!"); + } + connection.sendUDP(data); + } + } + } + }); + + client.connect(5000, host, tcpPort, udpPort); + + waitForThreads(5000); + } + + private void populateData (Data data, boolean isTCP) { + data.isTCP = isTCP; + + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < 3000; i++) + buffer.append('a'); + data.string = buffer.toString(); + + data.strings = new String[] {"abcdefghijklmnopqrstuvwxyz0123456789", "", null, "!@#$", "�����"}; + data.ints = new int[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; + data.shorts = new short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; + data.floats = new float[] {0, -0, 1, -1, 123456, -123456, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, + Float.MIN_VALUE}; + data.doubles = new double[] {0, -0, 1, -1, 123456, -123456, 0.1d, 0.2d, -0.3d, Math.PI, Double.MAX_VALUE, Double.MIN_VALUE}; + data.longs = new long[] {0, -0, 1, -1, 123456, -123456, 99999999999l, -99999999999l, Long.MAX_VALUE, Long.MIN_VALUE}; + data.bytes = new byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; + data.chars = new char[] {32345, 12345, 0, 1, 63, Character.MAX_VALUE, Character.MIN_VALUE}; + data.booleans = new boolean[] {true, false}; + data.Ints = new Integer[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; + data.Shorts = new Short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; + data.Floats = new Float[] {0f, -0f, 1f, -1f, 123456f, -123456f, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, + Float.MIN_VALUE}; + data.Doubles = new Double[] {0d, -0d, 1d, -1d, 123456d, -123456d, 0.1d, 0.2d, -0.3d, Math.PI, Double.MAX_VALUE, + Double.MIN_VALUE}; + data.Longs = new Long[] {0l, -0l, 1l, -1l, 123456l, -123456l, 99999999999l, -99999999999l, Long.MAX_VALUE, Long.MIN_VALUE}; + data.Bytes = new Byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; + data.Chars = new Character[] {32345, 12345, 0, 1, 63, Character.MAX_VALUE, Character.MIN_VALUE}; + data.Booleans = new Boolean[] {true, false}; + } + + private void register (Kryo kryo) { + kryo.register(String[].class); + kryo.register(int[].class); + kryo.register(short[].class); + kryo.register(float[].class); + kryo.register(double[].class); + kryo.register(long[].class); + kryo.register(byte[].class); + kryo.register(char[].class); + kryo.register(boolean[].class); + kryo.register(Integer[].class); + kryo.register(Short[].class); + kryo.register(Float[].class); + kryo.register(Double[].class); + kryo.register(Long[].class); + kryo.register(Byte[].class); + kryo.register(Character[].class); + kryo.register(Boolean[].class); + kryo.register(Data.class); + } + + static public class Data { + public String string; + public String[] strings; + public int[] ints; + public short[] shorts; + public float[] floats; + public double[] doubles; + public long[] longs; + public byte[] bytes; + public char[] chars; + public boolean[] booleans; + public Integer[] Ints; + public Short[] Shorts; + public Float[] Floats; + public Double[] Doubles; + public Long[] Longs; + public Byte[] Bytes; + public Character[] Chars; + public Boolean[] Booleans; + public boolean isTCP; + + public int hashCode () { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(Booleans); + result = prime * result + Arrays.hashCode(Bytes); + result = prime * result + Arrays.hashCode(Chars); + result = prime * result + Arrays.hashCode(Doubles); + result = prime * result + Arrays.hashCode(Floats); + result = prime * result + Arrays.hashCode(Ints); + result = prime * result + Arrays.hashCode(Longs); + result = prime * result + Arrays.hashCode(Shorts); + result = prime * result + Arrays.hashCode(booleans); + result = prime * result + Arrays.hashCode(bytes); + result = prime * result + Arrays.hashCode(chars); + result = prime * result + Arrays.hashCode(doubles); + result = prime * result + Arrays.hashCode(floats); + result = prime * result + Arrays.hashCode(ints); + result = prime * result + (isTCP ? 1231 : 1237); + result = prime * result + Arrays.hashCode(longs); + result = prime * result + Arrays.hashCode(shorts); + result = prime * result + ((string == null) ? 0 : string.hashCode()); + result = prime * result + Arrays.hashCode(strings); + return result; + } + + public boolean equals (Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Data other = (Data)obj; + if (!Arrays.equals(Booleans, other.Booleans)) return false; + if (!Arrays.equals(Bytes, other.Bytes)) return false; + if (!Arrays.equals(Chars, other.Chars)) return false; + if (!Arrays.equals(Doubles, other.Doubles)) return false; + if (!Arrays.equals(Floats, other.Floats)) return false; + if (!Arrays.equals(Ints, other.Ints)) return false; + if (!Arrays.equals(Longs, other.Longs)) return false; + if (!Arrays.equals(Shorts, other.Shorts)) return false; + if (!Arrays.equals(booleans, other.booleans)) return false; + if (!Arrays.equals(bytes, other.bytes)) return false; + if (!Arrays.equals(chars, other.chars)) return false; + if (!Arrays.equals(doubles, other.doubles)) return false; + if (!Arrays.equals(floats, other.floats)) return false; + if (!Arrays.equals(ints, other.ints)) return false; + if (isTCP != other.isTCP) return false; + if (!Arrays.equals(longs, other.longs)) return false; + if (!Arrays.equals(shorts, other.shorts)) return false; + if (string == null) { + if (other.string != null) return false; + } else if (!string.equals(other.string)) return false; + if (!Arrays.equals(strings, other.strings)) return false; + return true; + } + + public String toString () { + return "Data"; + } + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/PingTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/PingTest.java similarity index 96% rename from kryonet/test/com/esotericsoftware/kryonet/PingTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/PingTest.java index 419ba86c..58a7da30 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/PingTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/PingTest.java @@ -1,35 +1,35 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; - -import com.esotericsoftware.kryonet.FrameworkMessage.Ping; - -public class PingTest extends KryoNetTestCase { - public void testPing () throws IOException { - final Server server = new Server(); - startEndPoint(server); - server.bind(tcpPort); - - // ---- - - final Client client = new Client(); - startEndPoint(client); - client.addListener(new Listener() { - public void connected (Connection connection) { - client.updateReturnTripTime(); - } - - public void received (Connection connection, Object object) { - if (object instanceof Ping) { - Ping ping = (Ping)object; - if (ping.isReply) System.out.println("Ping: " + connection.getReturnTripTime()); - client.updateReturnTripTime(); - } - } - }); - client.connect(5000, host, tcpPort); - - waitForThreads(5000); - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; + +import com.esotericsoftware.kryonet.FrameworkMessage.Ping; + +public class PingTest extends KryoNetTestCase { + public void testPing () throws IOException { + final Server server = new Server(); + startEndPoint(server); + server.bind(tcpPort); + + // ---- + + final Client client = new Client(); + startEndPoint(client); + client.addListener(new Listener() { + public void connected (Connection connection) { + client.updateReturnTripTime(); + } + + public void received (Connection connection, Object object) { + if (object instanceof Ping) { + Ping ping = (Ping)object; + if (ping.isReply) System.out.println("Ping: " + connection.getReturnTripTime()); + client.updateReturnTripTime(); + } + } + }); + client.connect(5000, host, tcpPort); + + waitForThreads(5000); + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/ReconnectTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/ReconnectTest.java similarity index 96% rename from kryonet/test/com/esotericsoftware/kryonet/ReconnectTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/ReconnectTest.java index 444fb3a2..a557d55a 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/ReconnectTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/ReconnectTest.java @@ -1,55 +1,55 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.atomic.AtomicInteger; - -public class ReconnectTest extends KryoNetTestCase { - public void testReconnect () throws IOException { - final Timer timer = new Timer(); - - final Server server = new Server(); - startEndPoint(server); - server.bind(tcpPort); - server.addListener(new Listener() { - public void connected (final Connection connection) { - timer.schedule(new TimerTask() { - public void run () { - System.out.println("Disconnecting after 2 seconds."); - connection.close(); - } - }, 2000); - } - }); - - // ---- - - final AtomicInteger reconnetCount = new AtomicInteger(); - final Client client = new Client(); - startEndPoint(client); - client.addListener(new Listener() { - public void disconnected (Connection connection) { - if (reconnetCount.getAndIncrement() == 2) { - stopEndPoints(); - return; - } - new Thread() { - public void run () { - try { - System.out.println("Reconnecting: " + reconnetCount.get()); - client.reconnect(); - } catch (IOException ex) { - ex.printStackTrace(); - } - } - }.start(); - } - }); - client.connect(5000, host, tcpPort); - - waitForThreads(10000); - assertEquals(3, reconnetCount.getAndIncrement()); - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicInteger; + +public class ReconnectTest extends KryoNetTestCase { + public void testReconnect () throws IOException { + final Timer timer = new Timer(); + + final Server server = new Server(); + startEndPoint(server); + server.bind(tcpPort); + server.addListener(new Listener() { + public void connected (final Connection connection) { + timer.schedule(new TimerTask() { + public void run () { + System.out.println("Disconnecting after 2 seconds."); + connection.close(); + } + }, 2000); + } + }); + + // ---- + + final AtomicInteger reconnetCount = new AtomicInteger(); + final Client client = new Client(); + startEndPoint(client); + client.addListener(new Listener() { + public void disconnected (Connection connection) { + if (reconnetCount.getAndIncrement() == 2) { + stopEndPoints(); + return; + } + new Thread() { + public void run () { + try { + System.out.println("Reconnecting: " + reconnetCount.get()); + client.reconnect(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + }.start(); + } + }); + client.connect(5000, host, tcpPort); + + waitForThreads(10000); + assertEquals(3, reconnetCount.getAndIncrement()); + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/ReuseTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/ReuseTest.java similarity index 96% rename from kryonet/test/com/esotericsoftware/kryonet/ReuseTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/ReuseTest.java index 2f84cfd2..824632b1 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/ReuseTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/ReuseTest.java @@ -1,60 +1,60 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.util.concurrent.atomic.AtomicInteger; - -public class ReuseTest extends KryoNetTestCase { - public void testPingPong () throws IOException { - final AtomicInteger stringCount = new AtomicInteger(0); - - final Server server = new Server(); - startEndPoint(server); - server.addListener(new Listener() { - public void connected (Connection connection) { - connection.sendTCP("TCP from server"); - connection.sendUDP("UDP from server"); - } - - public void received (Connection connection, Object object) { - if (object instanceof String) { - stringCount.incrementAndGet(); - System.out.println(object); - } - } - }); - - // ---- - - final Client client = new Client(); - startEndPoint(client); - client.addListener(new Listener() { - public void connected (Connection connection) { - connection.sendTCP("TCP from client"); - connection.sendUDP("UDP from client"); - } - - public void received (Connection connection, Object object) { - if (object instanceof String) { - stringCount.incrementAndGet(); - System.out.println(object); - } - } - }); - - int count = 5; - for (int i = 0; i < count; i++) { - server.bind(tcpPort, udpPort); - client.connect(5000, host, tcpPort, udpPort); - try { - Thread.sleep(250); - } catch (InterruptedException ex) { - } - server.close(); - } - assertEquals(count * 2 * 2, stringCount.get()); - - stopEndPoints(); - waitForThreads(10000); - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +public class ReuseTest extends KryoNetTestCase { + public void testPingPong () throws IOException { + final AtomicInteger stringCount = new AtomicInteger(0); + + final Server server = new Server(); + startEndPoint(server); + server.addListener(new Listener() { + public void connected (Connection connection) { + connection.sendTCP("TCP from server"); + connection.sendUDP("UDP from server"); + } + + public void received (Connection connection, Object object) { + if (object instanceof String) { + stringCount.incrementAndGet(); + System.out.println(object); + } + } + }); + + // ---- + + final Client client = new Client(); + startEndPoint(client); + client.addListener(new Listener() { + public void connected (Connection connection) { + connection.sendTCP("TCP from client"); + connection.sendUDP("UDP from client"); + } + + public void received (Connection connection, Object object) { + if (object instanceof String) { + stringCount.incrementAndGet(); + System.out.println(object); + } + } + }); + + int count = 5; + for (int i = 0; i < count; i++) { + server.bind(tcpPort, udpPort); + client.connect(5000, host, tcpPort, udpPort); + try { + Thread.sleep(250); + } catch (InterruptedException ex) { + } + server.close(); + } + assertEquals(count * 2 * 2, stringCount.get()); + + stopEndPoints(); + waitForThreads(10000); + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/UnregisteredClassTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/UnregisteredClassTest.java similarity index 97% rename from kryonet/test/com/esotericsoftware/kryonet/UnregisteredClassTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/UnregisteredClassTest.java index 2978169e..ed0a4604 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/UnregisteredClassTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/UnregisteredClassTest.java @@ -1,172 +1,172 @@ - -package com.esotericsoftware.kryonet; - -import java.io.IOException; -import java.util.Arrays; - -public class UnregisteredClassTest extends KryoNetTestCase { - public void testPingPong () throws IOException { - final Data dataTCP = new Data(); - populateData(dataTCP, true); - final Data dataUDP = new Data(); - populateData(dataUDP, false); - - final Server server = new Server(16384, 8192); - server.getKryo().setRegistrationRequired(false); - startEndPoint(server); - server.bind(tcpPort, udpPort); - server.addListener(new Listener() { - public void connected (Connection connection) { - connection.sendTCP(dataTCP); - connection.sendUDP(dataUDP); // Note UDP ping pong stops if a UDP packet is lost. - } - - public void received (Connection connection, Object object) { - if (object instanceof Data) { - Data data = (Data)object; - if (data.isTCP) { - if (!data.equals(dataTCP)) throw new RuntimeException("Fail!"); - connection.sendTCP(data); - } else { - if (!data.equals(dataUDP)) throw new RuntimeException("Fail!"); - connection.sendUDP(data); - } - } - } - }); - - // ---- - - final Client client = new Client(16384, 8192); - client.getKryo().setRegistrationRequired(false); - startEndPoint(client); - client.addListener(new Listener() { - public void received (Connection connection, Object object) { - if (object instanceof Data) { - Data data = (Data)object; - if (data.isTCP) { - if (!data.equals(dataTCP)) throw new RuntimeException("Fail!"); - connection.sendTCP(data); - } else { - if (!data.equals(dataUDP)) throw new RuntimeException("Fail!"); - connection.sendUDP(data); - } - } - } - }); - - client.connect(5000, host, tcpPort, udpPort); - - waitForThreads(5000); - } - - private void populateData (Data data, boolean isTCP) { - data.isTCP = isTCP; - - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < 3000; i++) - buffer.append('a'); - data.string = buffer.toString(); - - data.strings = new String[] {"abcdefghijklmnopqrstuvwxyz0123456789", "", null, "!@#$", "�����"}; - data.ints = new int[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; - data.shorts = new short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; - data.floats = new float[] {0, -0, 1, -1, 123456, -123456, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, - Float.MIN_VALUE}; - data.doubles = new double[] {0, -0, 1, -1, 123456, -123456, 0.1d, 0.2d, -0.3d, Math.PI, Double.MAX_VALUE, Double.MIN_VALUE}; - data.longs = new long[] {0, -0, 1, -1, 123456, -123456, 99999999999l, -99999999999l, Long.MAX_VALUE, Long.MIN_VALUE}; - data.bytes = new byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; - data.chars = new char[] {32345, 12345, 0, 1, 63, Character.MAX_VALUE, Character.MIN_VALUE}; - data.booleans = new boolean[] {true, false}; - data.Ints = new Integer[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; - data.Shorts = new Short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; - data.Floats = new Float[] {0f, -0f, 1f, -1f, 123456f, -123456f, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, - Float.MIN_VALUE}; - data.Doubles = new Double[] {0d, -0d, 1d, -1d, 123456d, -123456d, 0.1d, 0.2d, -0.3d, Math.PI, Double.MAX_VALUE, - Double.MIN_VALUE}; - data.Longs = new Long[] {0l, -0l, 1l, -1l, 123456l, -123456l, 99999999999l, -99999999999l, Long.MAX_VALUE, Long.MIN_VALUE}; - data.Bytes = new Byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; - data.Chars = new Character[] {32345, 12345, 0, 1, 63, Character.MAX_VALUE, Character.MIN_VALUE}; - data.Booleans = new Boolean[] {true, false}; - } - - static public class Data { - public String string; - public String[] strings; - public int[] ints; - public short[] shorts; - public float[] floats; - public double[] doubles; - public long[] longs; - public byte[] bytes; - public char[] chars; - public boolean[] booleans; - public Integer[] Ints; - public Short[] Shorts; - public Float[] Floats; - public Double[] Doubles; - public Long[] Longs; - public Byte[] Bytes; - public Character[] Chars; - public Boolean[] Booleans; - public boolean isTCP; - - public int hashCode () { - final int prime = 31; - int result = 1; - result = prime * result + Arrays.hashCode(Booleans); - result = prime * result + Arrays.hashCode(Bytes); - result = prime * result + Arrays.hashCode(Chars); - result = prime * result + Arrays.hashCode(Doubles); - result = prime * result + Arrays.hashCode(Floats); - result = prime * result + Arrays.hashCode(Ints); - result = prime * result + Arrays.hashCode(Longs); - result = prime * result + Arrays.hashCode(Shorts); - result = prime * result + Arrays.hashCode(booleans); - result = prime * result + Arrays.hashCode(bytes); - result = prime * result + Arrays.hashCode(chars); - result = prime * result + Arrays.hashCode(doubles); - result = prime * result + Arrays.hashCode(floats); - result = prime * result + Arrays.hashCode(ints); - result = prime * result + (isTCP ? 1231 : 1237); - result = prime * result + Arrays.hashCode(longs); - result = prime * result + Arrays.hashCode(shorts); - result = prime * result + ((string == null) ? 0 : string.hashCode()); - result = prime * result + Arrays.hashCode(strings); - return result; - } - - public boolean equals (Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - Data other = (Data)obj; - if (!Arrays.equals(Booleans, other.Booleans)) return false; - if (!Arrays.equals(Bytes, other.Bytes)) return false; - if (!Arrays.equals(Chars, other.Chars)) return false; - if (!Arrays.equals(Doubles, other.Doubles)) return false; - if (!Arrays.equals(Floats, other.Floats)) return false; - if (!Arrays.equals(Ints, other.Ints)) return false; - if (!Arrays.equals(Longs, other.Longs)) return false; - if (!Arrays.equals(Shorts, other.Shorts)) return false; - if (!Arrays.equals(booleans, other.booleans)) return false; - if (!Arrays.equals(bytes, other.bytes)) return false; - if (!Arrays.equals(chars, other.chars)) return false; - if (!Arrays.equals(doubles, other.doubles)) return false; - if (!Arrays.equals(floats, other.floats)) return false; - if (!Arrays.equals(ints, other.ints)) return false; - if (isTCP != other.isTCP) return false; - if (!Arrays.equals(longs, other.longs)) return false; - if (!Arrays.equals(shorts, other.shorts)) return false; - if (string == null) { - if (other.string != null) return false; - } else if (!string.equals(other.string)) return false; - if (!Arrays.equals(strings, other.strings)) return false; - return true; - } - - public String toString () { - return "Data"; - } - } -} + +package com.esotericsoftware.kryonet; + +import java.io.IOException; +import java.util.Arrays; + +public class UnregisteredClassTest extends KryoNetTestCase { + public void testPingPong () throws IOException { + final Data dataTCP = new Data(); + populateData(dataTCP, true); + final Data dataUDP = new Data(); + populateData(dataUDP, false); + + final Server server = new Server(16384, 8192); + server.getKryo().setRegistrationRequired(false); + startEndPoint(server); + server.bind(tcpPort, udpPort); + server.addListener(new Listener() { + public void connected (Connection connection) { + connection.sendTCP(dataTCP); + connection.sendUDP(dataUDP); // Note UDP ping pong stops if a UDP packet is lost. + } + + public void received (Connection connection, Object object) { + if (object instanceof Data) { + Data data = (Data)object; + if (data.isTCP) { + if (!data.equals(dataTCP)) throw new RuntimeException("Fail!"); + connection.sendTCP(data); + } else { + if (!data.equals(dataUDP)) throw new RuntimeException("Fail!"); + connection.sendUDP(data); + } + } + } + }); + + // ---- + + final Client client = new Client(16384, 8192); + client.getKryo().setRegistrationRequired(false); + startEndPoint(client); + client.addListener(new Listener() { + public void received (Connection connection, Object object) { + if (object instanceof Data) { + Data data = (Data)object; + if (data.isTCP) { + if (!data.equals(dataTCP)) throw new RuntimeException("Fail!"); + connection.sendTCP(data); + } else { + if (!data.equals(dataUDP)) throw new RuntimeException("Fail!"); + connection.sendUDP(data); + } + } + } + }); + + client.connect(5000, host, tcpPort, udpPort); + + waitForThreads(5000); + } + + private void populateData (Data data, boolean isTCP) { + data.isTCP = isTCP; + + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < 3000; i++) + buffer.append('a'); + data.string = buffer.toString(); + + data.strings = new String[] {"abcdefghijklmnopqrstuvwxyz0123456789", "", null, "!@#$", "�����"}; + data.ints = new int[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; + data.shorts = new short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; + data.floats = new float[] {0, -0, 1, -1, 123456, -123456, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, + Float.MIN_VALUE}; + data.doubles = new double[] {0, -0, 1, -1, 123456, -123456, 0.1d, 0.2d, -0.3d, Math.PI, Double.MAX_VALUE, Double.MIN_VALUE}; + data.longs = new long[] {0, -0, 1, -1, 123456, -123456, 99999999999l, -99999999999l, Long.MAX_VALUE, Long.MIN_VALUE}; + data.bytes = new byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; + data.chars = new char[] {32345, 12345, 0, 1, 63, Character.MAX_VALUE, Character.MIN_VALUE}; + data.booleans = new boolean[] {true, false}; + data.Ints = new Integer[] {-1234567, 1234567, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}; + data.Shorts = new Short[] {-12345, 12345, -1, 0, 1, Short.MAX_VALUE, Short.MIN_VALUE}; + data.Floats = new Float[] {0f, -0f, 1f, -1f, 123456f, -123456f, 0.1f, 0.2f, -0.3f, (float)Math.PI, Float.MAX_VALUE, + Float.MIN_VALUE}; + data.Doubles = new Double[] {0d, -0d, 1d, -1d, 123456d, -123456d, 0.1d, 0.2d, -0.3d, Math.PI, Double.MAX_VALUE, + Double.MIN_VALUE}; + data.Longs = new Long[] {0l, -0l, 1l, -1l, 123456l, -123456l, 99999999999l, -99999999999l, Long.MAX_VALUE, Long.MIN_VALUE}; + data.Bytes = new Byte[] {-123, 123, -1, 0, 1, Byte.MAX_VALUE, Byte.MIN_VALUE}; + data.Chars = new Character[] {32345, 12345, 0, 1, 63, Character.MAX_VALUE, Character.MIN_VALUE}; + data.Booleans = new Boolean[] {true, false}; + } + + static public class Data { + public String string; + public String[] strings; + public int[] ints; + public short[] shorts; + public float[] floats; + public double[] doubles; + public long[] longs; + public byte[] bytes; + public char[] chars; + public boolean[] booleans; + public Integer[] Ints; + public Short[] Shorts; + public Float[] Floats; + public Double[] Doubles; + public Long[] Longs; + public Byte[] Bytes; + public Character[] Chars; + public Boolean[] Booleans; + public boolean isTCP; + + public int hashCode () { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(Booleans); + result = prime * result + Arrays.hashCode(Bytes); + result = prime * result + Arrays.hashCode(Chars); + result = prime * result + Arrays.hashCode(Doubles); + result = prime * result + Arrays.hashCode(Floats); + result = prime * result + Arrays.hashCode(Ints); + result = prime * result + Arrays.hashCode(Longs); + result = prime * result + Arrays.hashCode(Shorts); + result = prime * result + Arrays.hashCode(booleans); + result = prime * result + Arrays.hashCode(bytes); + result = prime * result + Arrays.hashCode(chars); + result = prime * result + Arrays.hashCode(doubles); + result = prime * result + Arrays.hashCode(floats); + result = prime * result + Arrays.hashCode(ints); + result = prime * result + (isTCP ? 1231 : 1237); + result = prime * result + Arrays.hashCode(longs); + result = prime * result + Arrays.hashCode(shorts); + result = prime * result + ((string == null) ? 0 : string.hashCode()); + result = prime * result + Arrays.hashCode(strings); + return result; + } + + public boolean equals (Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Data other = (Data)obj; + if (!Arrays.equals(Booleans, other.Booleans)) return false; + if (!Arrays.equals(Bytes, other.Bytes)) return false; + if (!Arrays.equals(Chars, other.Chars)) return false; + if (!Arrays.equals(Doubles, other.Doubles)) return false; + if (!Arrays.equals(Floats, other.Floats)) return false; + if (!Arrays.equals(Ints, other.Ints)) return false; + if (!Arrays.equals(Longs, other.Longs)) return false; + if (!Arrays.equals(Shorts, other.Shorts)) return false; + if (!Arrays.equals(booleans, other.booleans)) return false; + if (!Arrays.equals(bytes, other.bytes)) return false; + if (!Arrays.equals(chars, other.chars)) return false; + if (!Arrays.equals(doubles, other.doubles)) return false; + if (!Arrays.equals(floats, other.floats)) return false; + if (!Arrays.equals(ints, other.ints)) return false; + if (isTCP != other.isTCP) return false; + if (!Arrays.equals(longs, other.longs)) return false; + if (!Arrays.equals(shorts, other.shorts)) return false; + if (string == null) { + if (other.string != null) return false; + } else if (!string.equals(other.string)) return false; + if (!Arrays.equals(strings, other.strings)) return false; + return true; + } + + public String toString () { + return "Data"; + } + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/compress/DeflateTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/compress/DeflateTest.java similarity index 96% rename from kryonet/test/com/esotericsoftware/kryonet/compress/DeflateTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/compress/DeflateTest.java index bfb50b7c..e0011b75 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/compress/DeflateTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/compress/DeflateTest.java @@ -1,71 +1,71 @@ - -package com.esotericsoftware.kryonet.compress; - -import java.io.IOException; -import java.util.ArrayList; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.serializers.CollectionSerializer; -import com.esotericsoftware.kryo.serializers.DeflateSerializer; -import com.esotericsoftware.kryo.serializers.FieldSerializer; -import com.esotericsoftware.kryonet.Client; -import com.esotericsoftware.kryonet.Connection; -import com.esotericsoftware.kryonet.KryoNetTestCase; -import com.esotericsoftware.kryonet.Listener; -import com.esotericsoftware.kryonet.Server; - -public class DeflateTest extends KryoNetTestCase { - public void testDeflate () throws IOException { - final Server server = new Server(); - register(server.getKryo()); - - final SomeData data = new SomeData(); - data.text = "some text here aaaaaaaaaabbbbbbbbbbbcccccccccc"; - data.stuff = new short[] {1, 2, 3, 4, 5, 6, 7, 8}; - - final ArrayList a = new ArrayList(); - a.add(12); - a.add(null); - a.add(34); - - startEndPoint(server); - server.bind(tcpPort, udpPort); - server.addListener(new Listener() { - public void connected (Connection connection) { - server.sendToAllTCP(data); - connection.sendTCP(data); - connection.sendTCP(a); - } - }); - - // ---- - - final Client client = new Client(); - register(client.getKryo()); - startEndPoint(client); - client.addListener(new Listener() { - public void received (Connection connection, Object object) { - if (object instanceof SomeData) { - SomeData data = (SomeData)object; - System.out.println(data.stuff[3]); - } else if (object instanceof ArrayList) { - stopEndPoints(); - } - } - }); - client.connect(5000, host, tcpPort, udpPort); - - waitForThreads(); - } - - static public void register (Kryo kryo) { - kryo.register(short[].class); - kryo.register(SomeData.class, new DeflateSerializer(new FieldSerializer(kryo, SomeData.class))); - kryo.register(ArrayList.class, new CollectionSerializer()); - } - - static public class SomeData { - public String text; - public short[] stuff; - } -} + +package com.esotericsoftware.kryonet.compress; + +import java.io.IOException; +import java.util.ArrayList; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.serializers.CollectionSerializer; +import com.esotericsoftware.kryo.serializers.DeflateSerializer; +import com.esotericsoftware.kryo.serializers.FieldSerializer; +import com.esotericsoftware.kryonet.Client; +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.KryoNetTestCase; +import com.esotericsoftware.kryonet.Listener; +import com.esotericsoftware.kryonet.Server; + +public class DeflateTest extends KryoNetTestCase { + public void testDeflate () throws IOException { + final Server server = new Server(); + register(server.getKryo()); + + final SomeData data = new SomeData(); + data.text = "some text here aaaaaaaaaabbbbbbbbbbbcccccccccc"; + data.stuff = new short[] {1, 2, 3, 4, 5, 6, 7, 8}; + + final ArrayList a = new ArrayList(); + a.add(12); + a.add(null); + a.add(34); + + startEndPoint(server); + server.bind(tcpPort, udpPort); + server.addListener(new Listener() { + public void connected (Connection connection) { + server.sendToAllTCP(data); + connection.sendTCP(data); + connection.sendTCP(a); + } + }); + + // ---- + + final Client client = new Client(); + register(client.getKryo()); + startEndPoint(client); + client.addListener(new Listener() { + public void received (Connection connection, Object object) { + if (object instanceof SomeData) { + SomeData data = (SomeData)object; + System.out.println(data.stuff[3]); + } else if (object instanceof ArrayList) { + stopEndPoints(); + } + } + }); + client.connect(5000, host, tcpPort, udpPort); + + waitForThreads(); + } + + static public void register (Kryo kryo) { + kryo.register(short[].class); + kryo.register(SomeData.class, new DeflateSerializer(new FieldSerializer(kryo, SomeData.class))); + kryo.register(ArrayList.class, new CollectionSerializer()); + } + + static public class SomeData { + public String text; + public short[] stuff; + } +} diff --git a/kryonet/test/com/esotericsoftware/kryonet/rmi/RmiTest.java b/kryonet/src/test/java/com/esotericsoftware/kryonet/rmi/RmiTest.java similarity index 96% rename from kryonet/test/com/esotericsoftware/kryonet/rmi/RmiTest.java rename to kryonet/src/test/java/com/esotericsoftware/kryonet/rmi/RmiTest.java index 2234c8cf..f89aa659 100644 --- a/kryonet/test/com/esotericsoftware/kryonet/rmi/RmiTest.java +++ b/kryonet/src/test/java/com/esotericsoftware/kryonet/rmi/RmiTest.java @@ -1,205 +1,205 @@ - -package com.esotericsoftware.kryonet.rmi; - -import java.io.IOException; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryonet.Client; -import com.esotericsoftware.kryonet.Connection; -import com.esotericsoftware.kryonet.KryoNetTestCase; -import com.esotericsoftware.kryonet.Listener; -import com.esotericsoftware.kryonet.Server; - -public class RmiTest extends KryoNetTestCase { - public void testRMI () throws IOException { - Server server = new Server(); - register(server.getKryo()); - startEndPoint(server); - server.bind(tcpPort); - - final ObjectSpace serverObjectSpace = new ObjectSpace(); - final TestObjectImpl serverTestObject = new TestObjectImpl(4321); - serverObjectSpace.register((short)42, serverTestObject); - - server.addListener(new Listener() { - public void connected (final Connection connection) { - serverObjectSpace.addConnection(connection); - runTest(connection, 12, 1234); - } - - public void received (Connection connection, Object object) { - if (!(object instanceof MessageWithTestObject)) return; - MessageWithTestObject m = (MessageWithTestObject)object; - System.out.println(serverTestObject.value); - System.out.println(((TestObjectImpl)m.testObject).value); - assertEquals(4321f, m.testObject.other()); - stopEndPoints(2000); - } - }); - - // ---- - - Client client = new Client(); - register(client.getKryo()); - - ObjectSpace clientObjectSpace = new ObjectSpace(client); - final TestObjectImpl clientTestObject = new TestObjectImpl(1234); - clientObjectSpace.register((short)12, clientTestObject); - - startEndPoint(client); - client.addListener(new Listener() { - public void connected (final Connection connection) { - RmiTest.runTest(connection, 42, 4321); - } - - public void received (Connection connection, Object object) { - if (!(object instanceof MessageWithTestObject)) return; - MessageWithTestObject m = (MessageWithTestObject)object; - System.out.println(clientTestObject.value); - System.out.println(((TestObjectImpl)m.testObject).value); - assertEquals(1234f, m.testObject.other()); - stopEndPoints(2000); - } - }); - client.connect(5000, host, tcpPort); - - waitForThreads(); - } - - static public void runTest (final Connection connection, final int id, final float other) { - new Thread() { - public void run () { - TestObject test = ObjectSpace.getRemoteObject(connection, id, TestObject.class); - RemoteObject remoteObject = (RemoteObject)test; - // Default behavior. RMI is transparent, method calls behave like normal - // (return values and exceptions are returned, call is synchronous) - test.moo(); - test.moo("Cow"); - assertEquals(other, test.other()); - - // Test that RMI correctly waits for the remotely invoked method to exit - remoteObject.setResponseTimeout(5000); - test.moo("You should see this two seconds before...", 2000); - System.out.println("...This"); - remoteObject.setResponseTimeout(1000); - - // Try exception handling - boolean caught = false; - try { - test.asplode(); - } catch(UnsupportedOperationException ex) { - caught = true; - } - assertTrue(caught); - - // Return values are ignored, but exceptions are still dealt with properly - - remoteObject.setTransmitReturnValue(false); - test.moo("Baa"); - test.other(); - caught = false; - try { - test.asplode(); - } catch(UnsupportedOperationException ex) { - caught = true; - } - assertTrue(caught); - - // Non-blocking call that ignores the return value - remoteObject.setNonBlocking(true); - remoteObject.setTransmitReturnValue(false); - test.moo("Meow"); - assertEquals(0f, test.other()); - - // Non-blocking call that returns the return value - remoteObject.setTransmitReturnValue(true); - test.moo("Foo"); - - assertEquals(0f, test.other()); - assertEquals(other, remoteObject.waitForLastResponse()); - - assertEquals(0f, test.other()); - byte responseID = remoteObject.getLastResponseID(); - assertEquals(other, remoteObject.waitForResponse(responseID)); - - // Non-blocking call that errors out - remoteObject.setTransmitReturnValue(false); - test.asplode(); - assertEquals(remoteObject.waitForLastResponse().getClass(), UnsupportedOperationException.class); - - // Call will time out if non-blocking isn't working properly - remoteObject.setTransmitExceptions(false); - test.moo("Mooooooooo", 3000); - - // Test sending a reference to a remote object. - MessageWithTestObject m = new MessageWithTestObject(); - m.number = 678; - m.text = "sometext"; - m.testObject = ObjectSpace.getRemoteObject(connection, (short)id, TestObject.class); - connection.sendTCP(m); - } - }.start(); - } - - static public void register (Kryo kryo) { - kryo.register(TestObject.class); - kryo.register(MessageWithTestObject.class); - kryo.register(StackTraceElement.class); - kryo.register(StackTraceElement[].class); - kryo.register(UnsupportedOperationException.class); - ObjectSpace.registerClasses(kryo); - } - - static public interface TestObject { - public void asplode(); - - public void moo (); - - public void moo (String value); - - public void moo (String value, long delay); - - public float other (); - } - - static public class TestObjectImpl implements TestObject { - public long value = System.currentTimeMillis(); - private final float other; - - public TestObjectImpl (int other) { - this.other = other; - } - - public void asplode() { - throw new UnsupportedOperationException("Why would I do that?"); - } - - public void moo () { - System.out.println("Moo!"); - } - - public void moo (String value) { - System.out.println("Moo: " + value); - } - - public void moo (String value, long delay) { - System.out.println("Moo: " + value); - try { - Thread.sleep(delay); - } - catch (InterruptedException e) { - e.printStackTrace(); - } - } - - public float other () { - return other; - } - } - - static public class MessageWithTestObject { - public int number; - public String text; - public TestObject testObject; - } -} + +package com.esotericsoftware.kryonet.rmi; + +import java.io.IOException; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryonet.Client; +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.KryoNetTestCase; +import com.esotericsoftware.kryonet.Listener; +import com.esotericsoftware.kryonet.Server; + +public class RmiTest extends KryoNetTestCase { + public void testRMI () throws IOException { + Server server = new Server(); + register(server.getKryo()); + startEndPoint(server); + server.bind(tcpPort); + + final ObjectSpace serverObjectSpace = new ObjectSpace(); + final TestObjectImpl serverTestObject = new TestObjectImpl(4321); + serverObjectSpace.register((short)42, serverTestObject); + + server.addListener(new Listener() { + public void connected (final Connection connection) { + serverObjectSpace.addConnection(connection); + runTest(connection, 12, 1234); + } + + public void received (Connection connection, Object object) { + if (!(object instanceof MessageWithTestObject)) return; + MessageWithTestObject m = (MessageWithTestObject)object; + System.out.println(serverTestObject.value); + System.out.println(((TestObjectImpl)m.testObject).value); + assertEquals(4321f, m.testObject.other()); + stopEndPoints(2000); + } + }); + + // ---- + + Client client = new Client(); + register(client.getKryo()); + + ObjectSpace clientObjectSpace = new ObjectSpace(client); + final TestObjectImpl clientTestObject = new TestObjectImpl(1234); + clientObjectSpace.register((short)12, clientTestObject); + + startEndPoint(client); + client.addListener(new Listener() { + public void connected (final Connection connection) { + RmiTest.runTest(connection, 42, 4321); + } + + public void received (Connection connection, Object object) { + if (!(object instanceof MessageWithTestObject)) return; + MessageWithTestObject m = (MessageWithTestObject)object; + System.out.println(clientTestObject.value); + System.out.println(((TestObjectImpl)m.testObject).value); + assertEquals(1234f, m.testObject.other()); + stopEndPoints(2000); + } + }); + client.connect(5000, host, tcpPort); + + waitForThreads(); + } + + static public void runTest (final Connection connection, final int id, final float other) { + new Thread() { + public void run () { + TestObject test = ObjectSpace.getRemoteObject(connection, id, TestObject.class); + RemoteObject remoteObject = (RemoteObject)test; + // Default behavior. RMI is transparent, method calls behave like normal + // (return values and exceptions are returned, call is synchronous) + test.moo(); + test.moo("Cow"); + assertEquals(other, test.other()); + + // Test that RMI correctly waits for the remotely invoked method to exit + remoteObject.setResponseTimeout(5000); + test.moo("You should see this two seconds before...", 2000); + System.out.println("...This"); + remoteObject.setResponseTimeout(1000); + + // Try exception handling + boolean caught = false; + try { + test.asplode(); + } catch(UnsupportedOperationException ex) { + caught = true; + } + assertTrue(caught); + + // Return values are ignored, but exceptions are still dealt with properly + + remoteObject.setTransmitReturnValue(false); + test.moo("Baa"); + test.other(); + caught = false; + try { + test.asplode(); + } catch(UnsupportedOperationException ex) { + caught = true; + } + assertTrue(caught); + + // Non-blocking call that ignores the return value + remoteObject.setNonBlocking(true); + remoteObject.setTransmitReturnValue(false); + test.moo("Meow"); + assertEquals(0f, test.other()); + + // Non-blocking call that returns the return value + remoteObject.setTransmitReturnValue(true); + test.moo("Foo"); + + assertEquals(0f, test.other()); + assertEquals(other, remoteObject.waitForLastResponse()); + + assertEquals(0f, test.other()); + byte responseID = remoteObject.getLastResponseID(); + assertEquals(other, remoteObject.waitForResponse(responseID)); + + // Non-blocking call that errors out + remoteObject.setTransmitReturnValue(false); + test.asplode(); + assertEquals(remoteObject.waitForLastResponse().getClass(), UnsupportedOperationException.class); + + // Call will time out if non-blocking isn't working properly + remoteObject.setTransmitExceptions(false); + test.moo("Mooooooooo", 3000); + + // Test sending a reference to a remote object. + MessageWithTestObject m = new MessageWithTestObject(); + m.number = 678; + m.text = "sometext"; + m.testObject = ObjectSpace.getRemoteObject(connection, (short)id, TestObject.class); + connection.sendTCP(m); + } + }.start(); + } + + static public void register (Kryo kryo) { + kryo.register(TestObject.class); + kryo.register(MessageWithTestObject.class); + kryo.register(StackTraceElement.class); + kryo.register(StackTraceElement[].class); + kryo.register(UnsupportedOperationException.class); + ObjectSpace.registerClasses(kryo); + } + + static public interface TestObject { + public void asplode(); + + public void moo (); + + public void moo (String value); + + public void moo (String value, long delay); + + public float other (); + } + + static public class TestObjectImpl implements TestObject { + public long value = System.currentTimeMillis(); + private final float other; + + public TestObjectImpl (int other) { + this.other = other; + } + + public void asplode() { + throw new UnsupportedOperationException("Why would I do that?"); + } + + public void moo () { + System.out.println("Moo!"); + } + + public void moo (String value) { + System.out.println("Moo: " + value); + } + + public void moo (String value, long delay) { + System.out.println("Moo: " + value); + try { + Thread.sleep(delay); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public float other () { + return other; + } + } + + static public class MessageWithTestObject { + public int number; + public String text; + public TestObject testObject; + } +} From 257378f0c91543efd7f229b5febcdcb477ffd6b1 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Thu, 14 Nov 2013 13:39:44 +0100 Subject: [PATCH 8/9] Change POM to reflect Maven Central (Sonatype) requirements Also use now mavenised projects minlog and jsonbeans as normal Maven dependencies. TODO: switch to release version as soon as it is available on Maven Central. Subsequently this project can also be released on MC in a secondary step. --- kryonet/pom.xml | 325 ++++++++++++++++++++++++------------------------ 1 file changed, 161 insertions(+), 164 deletions(-) diff --git a/kryonet/pom.xml b/kryonet/pom.xml index 44495e38..24e7df4f 100644 --- a/kryonet/pom.xml +++ b/kryonet/pom.xml @@ -1,171 +1,168 @@ - - 4.0.0 + + + org.sonatype.oss + oss-parent + 7 + + 4.0.0 - com.esotericsoftware - kryonet - 2.21-SNAPSHOT + com.esotericsoftware + kryonet + 2.22-SNAPSHOT + jar - - src - test - bin - bin + kryonet + TCP and UDP client/server library for Java + https://github.com/EsotericSoftware/kryonet - - - com.googlecode.maven-download-plugin - maven-download-plugin - 1.1.0 - - - download-jsonbeans - validate - - wget - - - https://jsonbeans.googlecode.com/files/jsonbeans-0.5.zip - true - 8aca6c467d2a350434f08dd0dce4ba9a - - - - download-minlog - validate - - wget - - - https://minlog.googlecode.com/files/minlog-1.2.zip - true - 32552fea7ca0189dde2d15e6948acecb - - - - - - maven-install-plugin - 2.3.1 - - - install-jsonbean - validate - - install-file - - - ${project.build.directory}/jsonbeans-0.5/jsonbeans-0.5.jar - com.esotericsoftware - jsonbeans - 0.5 - jar - true - true - - - - install-minlog - validate - - install-file - - - ${project.build.directory}/minlog-1.2.jar - com.esotericsoftware - minlog - 1.2 - jar - true - true - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - deploy - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - - attach-javadocs - deploy - - jar - - - - - - + + + New BSD License + http://www.opensource.org/licenses/bsd-license.php + repo + + - - - com.esotericsoftware.kryo - kryo - 2.20 - - - org.ow2.asm - asm - 4.0 - - - org.objenesis - objenesis - 1.2 - - - com.esotericsoftware - jsonbeans - 0.5 - - - com.esotericsoftware - minlog - 1.2 - - - junit - junit - 4.11 - test - - + + + nathan.sweet + Nathan Sweet + nathan.sweet@gmail.com + + - - - my-domain.com - - scp://my-domain.com/var/www/kryonet/ - - - - releases - - http://my-domain.com/nexus/content/repositories/releases - - - - snapshots - - http://my-domain.com/nexus/content/repositories/snapshots - - - + + https://github.com/EsotericSoftware/kryonet + scm:git:https://github.com/EsotericSoftware/kryonet.git + scm:git:https://github.com/EsotericSoftware/kryonet.git + HEAD + + + + + true + + + 70CC1444 + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.4 + + ${gpgGuiPassphrase} + ${gpgKeyId} + + + + sign-artifacts + verify + + sign + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + package-sources + package + + jar-no-fork + test-jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + package-javadocs + package + + jar + test-jar + + + + + + maven-jar-plugin + 2.4 + + + package-tests + package + + test-jar + + + + + + + true + true + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.4.1 + + + + + + + com.esotericsoftware.kryo + kryo + 2.20 + + + org.ow2.asm + asm + 4.0 + + + org.objenesis + objenesis + 1.2 + + + com.esotericsoftware + jsonbeans + + 0.7-SNAPHOT + + + com.esotericsoftware + minlog + + 1.3-SNAPHOT + + + junit + junit + 4.11 + test + + From 0d74cc0da00874d0aadf0f7a349a2e3fb8bf3207 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Thu, 14 Nov 2013 15:07:25 +0100 Subject: [PATCH 9/9] Move everything under kryonet/ up one directory level --- kryonet/.classpath => .classpath | 30 ++-- .gitignore | 3 +- kryonet/.project => .project | 34 ++-- .../org.eclipse.jdt.core.prefs | 154 +++++++++--------- kryonet/license.txt => license.txt | 20 +-- kryonet/pom.xml => pom.xml | 0 kryonet/project.yaml => project.yaml | 8 +- .../kryonet/examples/chat/ChatClient.java | 0 .../kryonet/examples/chat/ChatServer.java | 0 .../kryonet/examples/chat/Network.java | 0 .../examples/chatrmi/ChatRmiClient.java | 0 .../examples/chatrmi/ChatRmiServer.java | 0 .../kryonet/examples/chatrmi/IChatFrame.java | 0 .../kryonet/examples/chatrmi/IPlayer.java | 0 .../kryonet/examples/chatrmi/Network.java | 0 .../kryonet/examples/position/Character.java | 0 .../kryonet/examples/position/Network.java | 0 .../examples/position/PositionClient.java | 0 .../examples/position/PositionServer.java | 0 .../com/esotericsoftware/kryonet/Client.java | 0 .../esotericsoftware/kryonet/Connection.java | 0 .../esotericsoftware/kryonet/EndPoint.java | 0 .../kryonet/FrameworkMessage.java | 0 .../kryonet/JsonSerialization.java | 0 .../kryonet/KryoNetException.java | 0 .../kryonet/KryoSerialization.java | 0 .../esotericsoftware/kryonet/Listener.java | 0 .../kryonet/Serialization.java | 0 .../com/esotericsoftware/kryonet/Server.java | 0 .../kryonet/TcpConnection.java | 0 .../kryonet/UdpConnection.java | 0 .../kryonet/rmi/ObjectSpace.java | 0 .../kryonet/rmi/RemoteObject.java | 0 .../kryonet/rmi/TimeoutException.java | 0 .../kryonet/util/InputStreamSender.java | 0 .../kryonet/util/TcpIdleSender.java | 0 .../esotericsoftware/kryonet/BufferTest.java | 0 .../kryonet/DiscoverHostTest.java | 0 .../kryonet/InputStreamSenderTest.java | 0 .../esotericsoftware/kryonet/JsonTest.java | 0 .../kryonet/KryoNetTestCase.java | 0 .../kryonet/MultipleServerTest.java | 0 .../kryonet/MultipleThreadTest.java | 0 .../kryonet/PingPongTest.java | 0 .../esotericsoftware/kryonet/PingTest.java | 0 .../kryonet/ReconnectTest.java | 0 .../esotericsoftware/kryonet/ReuseTest.java | 0 .../kryonet/UnregisteredClassTest.java | 0 .../kryonet/compress/DeflateTest.java | 0 .../esotericsoftware/kryonet/rmi/RmiTest.java | 0 50 files changed, 124 insertions(+), 125 deletions(-) rename kryonet/.classpath => .classpath (98%) rename kryonet/.project => .project (95%) rename {kryonet/.settings => .settings}/org.eclipse.jdt.core.prefs (98%) rename kryonet/license.txt => license.txt (99%) rename kryonet/pom.xml => pom.xml (100%) rename kryonet/project.yaml => project.yaml (94%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatClient.java (100%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatServer.java (100%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/chat/Network.java (100%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java (100%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java (100%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java (100%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java (100%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/Network.java (100%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/position/Character.java (100%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/position/Network.java (100%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/position/PositionClient.java (100%) rename {kryonet/src => src}/examples/java/com/esotericsoftware/kryonet/examples/position/PositionServer.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/Client.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/Connection.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/EndPoint.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/FrameworkMessage.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/JsonSerialization.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/KryoNetException.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/KryoSerialization.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/Listener.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/Serialization.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/Server.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/TcpConnection.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/UdpConnection.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/rmi/ObjectSpace.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/rmi/RemoteObject.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/rmi/TimeoutException.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/util/InputStreamSender.java (100%) rename {kryonet/src => src}/main/java/com/esotericsoftware/kryonet/util/TcpIdleSender.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/BufferTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/DiscoverHostTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/InputStreamSenderTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/JsonTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/KryoNetTestCase.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/MultipleServerTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/MultipleThreadTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/PingPongTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/PingTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/ReconnectTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/ReuseTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/UnregisteredClassTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/compress/DeflateTest.java (100%) rename {kryonet/src => src}/test/java/com/esotericsoftware/kryonet/rmi/RmiTest.java (100%) diff --git a/kryonet/.classpath b/.classpath similarity index 98% rename from kryonet/.classpath rename to .classpath index 51e37360..f345baa7 100644 --- a/kryonet/.classpath +++ b/.classpath @@ -1,15 +1,15 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore index ce341e4a..ec105515 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -kryonet/bin/ -kryonet/target/ +target/ .idea/ *.iml diff --git a/kryonet/.project b/.project similarity index 95% rename from kryonet/.project rename to .project index 97804fd8..fdaa1ae5 100644 --- a/kryonet/.project +++ b/.project @@ -1,17 +1,17 @@ - - - kryonet - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - + + + kryonet + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/kryonet/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs similarity index 98% rename from kryonet/.settings/org.eclipse.jdt.core.prefs rename to .settings/org.eclipse.jdt.core.prefs index 4bad154d..cd9caeb1 100644 --- a/kryonet/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,77 +1,77 @@ -#Thu Jan 21 00:52:05 PST 2010 -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.doc.comment.support=enabled -org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning -org.eclipse.jdt.core.compiler.problem.autoboxing=ignore -org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning -org.eclipse.jdt.core.compiler.problem.deadCode=ignore -org.eclipse.jdt.core.compiler.problem.deprecation=ignore -org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled -org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled -org.eclipse.jdt.core.compiler.problem.discouragedReference=warning -org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore -org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore -org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled -org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore -org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning -org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning -org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning -org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore -org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore -org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning -org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private -org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore -org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning -org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore -org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore -org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore -org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled -org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public -org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag -org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore -org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled -org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore -org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore -org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore -org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning -org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning -org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore -org.eclipse.jdt.core.compiler.problem.nullReference=warning -org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning -org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore -org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning -org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore -org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore -org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore -org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore -org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled -org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning -org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled -org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning -org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning -org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore -org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore -org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning -org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore -org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore -org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled -org.eclipse.jdt.core.compiler.problem.unusedImport=ignore -org.eclipse.jdt.core.compiler.problem.unusedLabel=warning -org.eclipse.jdt.core.compiler.problem.unusedLocal=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled -org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=ignore -org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning -org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +#Thu Jan 21 00:52:05 PST 2010 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=ignore +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled +org.eclipse.jdt.core.compiler.problem.unusedImport=ignore +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=ignore +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning diff --git a/kryonet/license.txt b/license.txt similarity index 99% rename from kryonet/license.txt rename to license.txt index 5a57ac7a..e1cd8847 100644 --- a/kryonet/license.txt +++ b/license.txt @@ -1,10 +1,10 @@ -Copyright (c) 2008, Nathan Sweet -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Copyright (c) 2008, Nathan Sweet +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/kryonet/pom.xml b/pom.xml similarity index 100% rename from kryonet/pom.xml rename to pom.xml diff --git a/kryonet/project.yaml b/project.yaml similarity index 94% rename from kryonet/project.yaml rename to project.yaml index 9c32be34..b3f36f80 100644 --- a/kryonet/project.yaml +++ b/project.yaml @@ -1,4 +1,4 @@ -version: 2.18 ---- -Build.build(project); -Build.oneJAR(project); +version: 2.18 +--- +Build.build(project); +Build.oneJAR(project); diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatClient.java b/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatClient.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatClient.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatClient.java diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatServer.java b/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatServer.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatServer.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/chat/ChatServer.java diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/Network.java b/src/examples/java/com/esotericsoftware/kryonet/examples/chat/Network.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chat/Network.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/chat/Network.java diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java b/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiClient.java diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java b/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/ChatRmiServer.java diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java b/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IChatFrame.java diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java b/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/IPlayer.java diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/Network.java b/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/Network.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/Network.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/chatrmi/Network.java diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/Character.java b/src/examples/java/com/esotericsoftware/kryonet/examples/position/Character.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/Character.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/position/Character.java diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/Network.java b/src/examples/java/com/esotericsoftware/kryonet/examples/position/Network.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/Network.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/position/Network.java diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionClient.java b/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionClient.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionClient.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionClient.java diff --git a/kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionServer.java b/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionServer.java similarity index 100% rename from kryonet/src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionServer.java rename to src/examples/java/com/esotericsoftware/kryonet/examples/position/PositionServer.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/Client.java b/src/main/java/com/esotericsoftware/kryonet/Client.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/Client.java rename to src/main/java/com/esotericsoftware/kryonet/Client.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/Connection.java b/src/main/java/com/esotericsoftware/kryonet/Connection.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/Connection.java rename to src/main/java/com/esotericsoftware/kryonet/Connection.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/EndPoint.java b/src/main/java/com/esotericsoftware/kryonet/EndPoint.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/EndPoint.java rename to src/main/java/com/esotericsoftware/kryonet/EndPoint.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/FrameworkMessage.java b/src/main/java/com/esotericsoftware/kryonet/FrameworkMessage.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/FrameworkMessage.java rename to src/main/java/com/esotericsoftware/kryonet/FrameworkMessage.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/JsonSerialization.java b/src/main/java/com/esotericsoftware/kryonet/JsonSerialization.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/JsonSerialization.java rename to src/main/java/com/esotericsoftware/kryonet/JsonSerialization.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/KryoNetException.java b/src/main/java/com/esotericsoftware/kryonet/KryoNetException.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/KryoNetException.java rename to src/main/java/com/esotericsoftware/kryonet/KryoNetException.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/KryoSerialization.java b/src/main/java/com/esotericsoftware/kryonet/KryoSerialization.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/KryoSerialization.java rename to src/main/java/com/esotericsoftware/kryonet/KryoSerialization.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/Listener.java b/src/main/java/com/esotericsoftware/kryonet/Listener.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/Listener.java rename to src/main/java/com/esotericsoftware/kryonet/Listener.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/Serialization.java b/src/main/java/com/esotericsoftware/kryonet/Serialization.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/Serialization.java rename to src/main/java/com/esotericsoftware/kryonet/Serialization.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/Server.java b/src/main/java/com/esotericsoftware/kryonet/Server.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/Server.java rename to src/main/java/com/esotericsoftware/kryonet/Server.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/TcpConnection.java b/src/main/java/com/esotericsoftware/kryonet/TcpConnection.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/TcpConnection.java rename to src/main/java/com/esotericsoftware/kryonet/TcpConnection.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/UdpConnection.java b/src/main/java/com/esotericsoftware/kryonet/UdpConnection.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/UdpConnection.java rename to src/main/java/com/esotericsoftware/kryonet/UdpConnection.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/ObjectSpace.java b/src/main/java/com/esotericsoftware/kryonet/rmi/ObjectSpace.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/ObjectSpace.java rename to src/main/java/com/esotericsoftware/kryonet/rmi/ObjectSpace.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/RemoteObject.java b/src/main/java/com/esotericsoftware/kryonet/rmi/RemoteObject.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/RemoteObject.java rename to src/main/java/com/esotericsoftware/kryonet/rmi/RemoteObject.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/TimeoutException.java b/src/main/java/com/esotericsoftware/kryonet/rmi/TimeoutException.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/rmi/TimeoutException.java rename to src/main/java/com/esotericsoftware/kryonet/rmi/TimeoutException.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/util/InputStreamSender.java b/src/main/java/com/esotericsoftware/kryonet/util/InputStreamSender.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/util/InputStreamSender.java rename to src/main/java/com/esotericsoftware/kryonet/util/InputStreamSender.java diff --git a/kryonet/src/main/java/com/esotericsoftware/kryonet/util/TcpIdleSender.java b/src/main/java/com/esotericsoftware/kryonet/util/TcpIdleSender.java similarity index 100% rename from kryonet/src/main/java/com/esotericsoftware/kryonet/util/TcpIdleSender.java rename to src/main/java/com/esotericsoftware/kryonet/util/TcpIdleSender.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/BufferTest.java b/src/test/java/com/esotericsoftware/kryonet/BufferTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/BufferTest.java rename to src/test/java/com/esotericsoftware/kryonet/BufferTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/DiscoverHostTest.java b/src/test/java/com/esotericsoftware/kryonet/DiscoverHostTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/DiscoverHostTest.java rename to src/test/java/com/esotericsoftware/kryonet/DiscoverHostTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/InputStreamSenderTest.java b/src/test/java/com/esotericsoftware/kryonet/InputStreamSenderTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/InputStreamSenderTest.java rename to src/test/java/com/esotericsoftware/kryonet/InputStreamSenderTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/JsonTest.java b/src/test/java/com/esotericsoftware/kryonet/JsonTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/JsonTest.java rename to src/test/java/com/esotericsoftware/kryonet/JsonTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/KryoNetTestCase.java b/src/test/java/com/esotericsoftware/kryonet/KryoNetTestCase.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/KryoNetTestCase.java rename to src/test/java/com/esotericsoftware/kryonet/KryoNetTestCase.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/MultipleServerTest.java b/src/test/java/com/esotericsoftware/kryonet/MultipleServerTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/MultipleServerTest.java rename to src/test/java/com/esotericsoftware/kryonet/MultipleServerTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/MultipleThreadTest.java b/src/test/java/com/esotericsoftware/kryonet/MultipleThreadTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/MultipleThreadTest.java rename to src/test/java/com/esotericsoftware/kryonet/MultipleThreadTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/PingPongTest.java b/src/test/java/com/esotericsoftware/kryonet/PingPongTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/PingPongTest.java rename to src/test/java/com/esotericsoftware/kryonet/PingPongTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/PingTest.java b/src/test/java/com/esotericsoftware/kryonet/PingTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/PingTest.java rename to src/test/java/com/esotericsoftware/kryonet/PingTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/ReconnectTest.java b/src/test/java/com/esotericsoftware/kryonet/ReconnectTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/ReconnectTest.java rename to src/test/java/com/esotericsoftware/kryonet/ReconnectTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/ReuseTest.java b/src/test/java/com/esotericsoftware/kryonet/ReuseTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/ReuseTest.java rename to src/test/java/com/esotericsoftware/kryonet/ReuseTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/UnregisteredClassTest.java b/src/test/java/com/esotericsoftware/kryonet/UnregisteredClassTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/UnregisteredClassTest.java rename to src/test/java/com/esotericsoftware/kryonet/UnregisteredClassTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/compress/DeflateTest.java b/src/test/java/com/esotericsoftware/kryonet/compress/DeflateTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/compress/DeflateTest.java rename to src/test/java/com/esotericsoftware/kryonet/compress/DeflateTest.java diff --git a/kryonet/src/test/java/com/esotericsoftware/kryonet/rmi/RmiTest.java b/src/test/java/com/esotericsoftware/kryonet/rmi/RmiTest.java similarity index 100% rename from kryonet/src/test/java/com/esotericsoftware/kryonet/rmi/RmiTest.java rename to src/test/java/com/esotericsoftware/kryonet/rmi/RmiTest.java