# HG changeset patch # User Medo <smaxein@googlemail.com> # Date 1345243671 -7200 # Node ID 763d3961400b294e5d6f231f3a5bafbaba2b377a # Parent ed1d52c5aa94c7b84d130d1273e35bedba8d53d8 Hedgeroid: Frantic scrabbling toward the deadline - Added activities for weapon/scheme editors (unfinished) - Completed tablet version of netroom activity - Added map preview - Fixed default team files having the wrong names - Restructuring - Updated frontlib JNA bindings to respect the latest frontlib changes diff -r ed1d52c5aa94 -r 763d3961400b hedgewars/hwLibrary.pas --- a/hedgewars/hwLibrary.pas Sat Aug 18 00:22:33 2012 +0200 +++ b/hedgewars/hwLibrary.pas Sat Aug 18 00:47:51 2012 +0200 @@ -99,10 +99,14 @@ JNI_HW_versionInfoVersion := envderef^.NewStringUTF(env, PChar(cVersionString)); end; -function JNI_HW_GenLandPreview(env: PJNIEnv; c: JClass; port: JInt):JInt; cdecl; +procedure JNI_HW_GenLandPreview(env: PJNIEnv; c: JClass; port: JInt); cdecl; begin GenLandPreview(port); - JNI_HW_GenLandPreview := port; +end; + +procedure JNI_HW_Terminate(env: PJNIEnv; c: JClass); cdecl; +begin + HW_terminate(false); end; exports @@ -112,7 +116,7 @@ HW_getNumberOfweapons name Java_Prefix + 'HWgetNumberOfWeapons', HW_getMaxNumberOfHogs name Java_Prefix + 'HWgetMaxNumberOfHogs', HW_getMaxNumberOfTeams name Java_Prefix + 'HWgetMaxNumberOfTeams', - HW_terminate name Java_Prefix + 'HWterminate', + JNI_HW_Terminate name Java_Prefix + 'HWterminate', Game; {$ELSE} exports diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/AndroidManifest.xml --- a/project_files/Android-build/SDL-android-project/AndroidManifest.xml Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/AndroidManifest.xml Sat Aug 18 00:47:51 2012 +0200 @@ -67,16 +67,40 @@ android:windowSoftInputMode="stateUnchanged" > </activity> <activity - android:name=".netplay.LobbyActivity" + android:name=".LobbyActivity" android:label="@string/title_activity_lobby" android:screenOrientation="landscape" android:windowSoftInputMode="adjustPan" > </activity> <activity - android:name=".netplay.RoomActivity" + android:name=".RoomActivity" android:label="@string/title_activity_room" android:screenOrientation="landscape" android:windowSoftInputMode="adjustPan" > </activity> + <activity + android:name=".WeaponsetListActivity" + android:label="@string/title_activity_weaponset_list" + android:screenOrientation="landscape" + android:windowSoftInputMode="adjustPan" > + </activity> + <activity + android:name=".WeaponsetCreatorActivity" + android:label="@string/title_activity_weaponset_creator" + android:screenOrientation="landscape" + android:windowSoftInputMode="adjustPan" > + </activity> + <activity + android:name=".SchemeListActivity" + android:label="@string/title_activity_scheme_list" + android:screenOrientation="landscape" + android:windowSoftInputMode="adjustPan" > + </activity> + <activity + android:name=".SchemeCreatorActivity" + android:label="@string/title_activity_scheme_creator" + android:screenOrientation="landscape" + android:windowSoftInputMode="adjustPan" > + </activity> </application> </manifest> \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/assets/Data/metasettings.ini --- a/project_files/Android-build/SDL-android-project/assets/Data/metasettings.ini Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,234 +0,0 @@ -[mod0] -name=fortsmode -bitmaskindex=12 - -[mod1] -name=divteams -bitmaskindex=4 - -[mod2] -name=solidland -bitmaskindex=2 - -[mod3] -name=border -bitmaskindex=3 - -[mod4] -name=lowgrav -bitmaskindex=5 - -[mod5] -name=laser -bitmaskindex=6 - -[mod6] -name=invulnerability -bitmaskindex=7 - -[mod7] -name=resethealth -bitmaskindex=8 - -[mod8] -name=vampiric -bitmaskindex=9 - -[mod9] -name=karma -bitmaskindex=10 - -[mod10] -name=artillery -bitmaskindex=11 - -[mod11] -name=randomorder -bitmaskindex=13 - -[mod12] -name=king -bitmaskindex=14 - -[mod13] -name=placehog -bitmaskindex=15 - -[mod14] -name=sharedammo -bitmaskindex=16 - -[mod15] -name=disablegirders -bitmaskindex=17 - -[mod16] -name=disablelandobjects -bitmaskindex=18 - -[mod17] -name=aisurvival -bitmaskindex=19 - -[mod18] -name=infattack -bitmaskindex=20 - -[mod19] -name=resetweps -bitmaskindex=21 - -[mod20] -name=perhogammo -bitmaskindex=22 - -[mod21] -name=disablewind -bitmaskindex=23 - -[mod22] -name=morewind -bitmaskindex=24 - -[mod23] -name=tagteam -bitmaskindex=25 - -[mod24] -name=bottomborder -bitmaskindex=26 - - -[setting0] -name=damagefactor -times1000=false -command=e$damagepct -maxmeansinfinity=false -min=10 -max=300 -default=100 - -[setting1] -name=turntime -times1000=true -command=e$turntime -maxmeansinfinity=true -min=1 -max=9999 -default=45 - -[setting2] -name=health -times1000=false -maxmeansinfinity=false -min=50 -max=200 -default=100 - -[setting3] -name=suddendeath -times1000=false -command=e$sd_turns -maxmeansinfinity=true -min=0 -max=50 -default=15 - -[setting4] -name=caseprobability -times1000=false -command=e$casefreq -maxmeansinfinity=false -min=0 -max=9 -default=5 - -[setting5] -name=minestime -times1000=true -command=e$minestime -maxmeansinfinity=false -min=-1 -max=5 -default=3 - -[setting6] -name=minesnum -times1000=false -command=e$minesnum -maxmeansinfinity=false -min=0 -max=80 -default=4 - -[setting7] -name=minedudpct -times1000=false -command=e$minedudpct -maxmeansinfinity=false -min=0 -max=100 -default=0 - -[setting8] -name=explosives -times1000=false -command=e$explosives -maxmeansinfinity=false -min=0 -max=40 -default=2 - -[setting9] -name=healthprobability -times1000=false -command=e$healthprob -maxmeansinfinity=false -min=0 -max=100 -default=35 - -[setting10] -name=healthcaseamount -times1000=false -command=e$hcaseamount -maxmeansinfinity=false -min=0 -max=200 -default=25 - -[setting11] -name=waterrise -times1000=false -command=e$waterrise -maxmeansinfinity=false -min=0 -max=100 -default=47 - -[setting12] -name=healthdecrease -times1000=false -command=e$healthdec -maxmeansinfinity=false -min=0 -max=100 -default=5 - -[setting13] -name=ropepct -times1000=false -command=e$ropepct -maxmeansinfinity=false -min=25 -max=999 -default=100 - -[setting14] -name=getawaytime -times1000=false -command=e$getawaytime -maxmeansinfinity=false -min=0 -max=999 -default=100 diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/assets/assetsversion.txt --- a/project_files/Android-build/SDL-android-project/assets/assetsversion.txt Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/assets/assetsversion.txt Sat Aug 18 00:47:51 2012 +0200 @@ -1,1 +1,1 @@ -5 \ No newline at end of file +7 \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount.xml Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,10 @@ + <level-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:maxLevel="1" android:drawable="@drawable/hogcount1" /> + <item android:maxLevel="2" android:drawable="@drawable/hogcount2" /> + <item android:maxLevel="3" android:drawable="@drawable/hogcount3" /> + <item android:maxLevel="4" android:drawable="@drawable/hogcount4" /> + <item android:maxLevel="5" android:drawable="@drawable/hogcount5" /> + <item android:maxLevel="6" android:drawable="@drawable/hogcount6" /> + <item android:maxLevel="7" android:drawable="@drawable/hogcount7" /> + <item android:maxLevel="8" android:drawable="@drawable/hogcount8" /> + </level-list> \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount1.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount1.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount2.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount2.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount3.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount3.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount4.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount4.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount5.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount5.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount6.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount6.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount7.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount7.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount8.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount8.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_local_by_level.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_local_by_level.xml Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,8 @@ +<level-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:maxLevel="0" android:drawable="@drawable/human" /> + <item android:maxLevel="1" android:drawable="@drawable/bot5" /> + <item android:maxLevel="2" android:drawable="@drawable/bot4" /> + <item android:maxLevel="3" android:drawable="@drawable/bot3" /> + <item android:maxLevel="4" android:drawable="@drawable/bot2" /> + <item android:maxLevel="5" android:drawable="@drawable/bot1" /> +</level-list> \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot1.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot1.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot2.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot2.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot3.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot3.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot4.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot4.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot5.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot5.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_by_level.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_by_level.xml Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,8 @@ +<level-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:maxLevel="0" android:drawable="@drawable/team_net_human" /> + <item android:maxLevel="1" android:drawable="@drawable/team_net_bot5" /> + <item android:maxLevel="2" android:drawable="@drawable/team_net_bot4" /> + <item android:maxLevel="3" android:drawable="@drawable/team_net_bot3" /> + <item android:maxLevel="4" android:drawable="@drawable/team_net_bot2" /> + <item android:maxLevel="5" android:drawable="@drawable/team_net_bot1" /> +</level-list> \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_human.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_human.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount.xml --- a/project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount.xml Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ - <level-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:maxLevel="0" android:drawable="@drawable/teams_number0" /> - <item android:maxLevel="1" android:drawable="@drawable/teams_number1" /> - <item android:maxLevel="2" android:drawable="@drawable/teams_number2" /> - <item android:maxLevel="3" android:drawable="@drawable/teams_number3" /> - <item android:maxLevel="4" android:drawable="@drawable/teams_number4" /> - <item android:maxLevel="5" android:drawable="@drawable/teams_number5" /> - <item android:maxLevel="6" android:drawable="@drawable/teams_number6" /> - <item android:maxLevel="7" android:drawable="@drawable/teams_number7" /> - <item android:maxLevel="8" android:drawable="@drawable/teams_number8" /> - <item android:maxLevel="9" android:drawable="@drawable/teams_number9" /> - </level-list> \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount0.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount0.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount1.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount1.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount2.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount2.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount3.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount3.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount4.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount4.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount5.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount5.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount6.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount6.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount7.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount7.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount8.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount8.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount9.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount9.png has changed diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/drawable-mdpi/teams_number.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/res/drawable-mdpi/teams_number.xml Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,12 @@ + <level-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:maxLevel="0" android:drawable="@drawable/teams_number0" /> + <item android:maxLevel="1" android:drawable="@drawable/teams_number1" /> + <item android:maxLevel="2" android:drawable="@drawable/teams_number2" /> + <item android:maxLevel="3" android:drawable="@drawable/teams_number3" /> + <item android:maxLevel="4" android:drawable="@drawable/teams_number4" /> + <item android:maxLevel="5" android:drawable="@drawable/teams_number5" /> + <item android:maxLevel="6" android:drawable="@drawable/teams_number6" /> + <item android:maxLevel="7" android:drawable="@drawable/teams_number7" /> + <item android:maxLevel="8" android:drawable="@drawable/teams_number8" /> + <item android:maxLevel="9" android:drawable="@drawable/teams_number9" /> + </level-list> \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/layout/activity_netroom.xml --- a/project_files/Android-build/SDL-android-project/res/layout/activity_netroom.xml Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/res/layout/activity_netroom.xml Sat Aug 18 00:47:51 2012 +0200 @@ -21,7 +21,7 @@ android:layout_marginBottom="10dp" android:baselineAligned="false" android:minHeight="200dp" > - + <FrameLayout android:id="@+id/mapFrame" android:layout_width="0dp" @@ -30,14 +30,12 @@ android:layout_weight="1" android:background="@drawable/box" > - <!-- - <fragment + <fragment android:id="@+id/mapFragment" - android:layout_width="fill_parent" - android:layout_height="fill_parent" + android:layout_width="fill_parent" + android:layout_height="fill_parent" class="org.hedgewars.hedgeroid.netplay.MapFragment" tools:layout="@layout/fragment_map" /> - --> </FrameLayout> <FrameLayout @@ -48,14 +46,12 @@ android:layout_weight="1" android:background="@drawable/box" > - <!-- - <fragment + <fragment android:id="@+id/settingsFragment" - android:layout_width="fill_parent" - android:layout_height="fill_parent" + android:layout_width="fill_parent" + android:layout_height="fill_parent" class="org.hedgewars.hedgeroid.netplay.SettingsFragment" tools:layout="@layout/fragment_settings" /> - --> </FrameLayout> <FrameLayout diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/layout/activity_schemelist.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/res/layout/activity_schemelist.xml Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" > + + <ListView + android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:cacheColorHint="@android:color/transparent" /> + + <Button + android:id="@+id/addButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/schemelist_add_button_text" + android:background="@drawable/button" /> + +</LinearLayout> \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/layout/activity_weaponsetlist.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/res/layout/activity_weaponsetlist.xml Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" > + + <TextView + android:id="@android:id/empty" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:text="@string/weaponsetlist_empty" /> + + <ListView + android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:cacheColorHint="@android:color/transparent" /> + + <Button + android:id="@+id/addButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/button" + android:text="@string/weaponsetlist_add_button_text" /> + +</LinearLayout> \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/layout/fragment_map.xml --- a/project_files/Android-build/SDL-android-project/res/layout/fragment_map.xml Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/res/layout/fragment_map.xml Sat Aug 18 00:47:51 2012 +0200 @@ -17,8 +17,8 @@ android:layout_centerHorizontal="true" android:layout_margin="5dip" android:background="@drawable/box" - android:scaleType="fitXY" - android:src="@drawable/backbutton" /> + android:scaleType="fitCenter" + android:src="@drawable/roomlist_preparing" /> <TableLayout android:id="@+id/gameOptions" @@ -30,32 +30,56 @@ <TableRow> <TextView - android:id="@+id/txtMap" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/map_map" /> + android:text="@string/map_gen" /> <Spinner - android:id="@+id/spinMaps" + android:id="@+id/spinMapType" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/dropdown" /> </TableRow> - <TableRow> - + <TableRow android:id="@+id/rowMapName"> <TextView - android:id="@+id/txtType" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/map_type" /> + android:layout_gravity="center_vertical" + android:text="@string/map_name" /> <Spinner - android:id="@+id/spinType" - android:layout_width="wrap_content" + android:id="@+id/spinMapName" + android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/dropdown" /> </TableRow> + <TableRow android:id="@+id/rowTemplateFilter"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:text="@string/map_template" /> + + <Spinner + android:id="@+id/spinTemplateFilter" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:background="@drawable/dropdown" /> + </TableRow> + <TableRow android:id="@+id/rowMazeSize"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:text="@string/map_maze_size" /> + + <Spinner + android:id="@+id/spinMazeSize" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:background="@drawable/dropdown" /> + </TableRow> </TableLayout> </RelativeLayout> \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/layout/listview_item.xml --- a/project_files/Android-build/SDL-android-project/res/layout/listview_item.xml Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/res/layout/listview_item.xml Sat Aug 18 00:47:51 2012 +0200 @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/text1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="10dip" diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/layout/listview_team.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/res/layout/listview_team.xml Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingTop="4dp" + android:paddingBottom="4dp" > + <TextView + android:id="@android:id/text1" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|left" + android:layout_weight="1" + android:gravity="center_vertical" + android:drawablePadding="5dp" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + <ImageButton + android:id="@+id/colorButton" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_gravity="center_vertical|right" + android:src="#fff" + android:padding="8dp" + android:contentDescription="@string/teamlist_color_button_description" /> + + <ImageButton + android:id="@+id/hogCountButton" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_gravity="center_vertical|right" + android:src="@drawable/hogcount" + android:scaleType="centerCrop" + android:padding="0dp" + android:contentDescription="@string/teamlist_hogcount_button_description" /> +</LinearLayout> \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/layout/starting_game.xml --- a/project_files/Android-build/SDL-android-project/res/layout/starting_game.xml Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/res/layout/starting_game.xml Sat Aug 18 00:47:51 2012 +0200 @@ -147,7 +147,7 @@ android:adjustViewBounds="true" android:scaleType="centerInside" android:background="@android:color/transparent" - android:src="@drawable/teamcount"/> + android:src="@drawable/teams_number"/> </LinearLayout> diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/layout/team_selection_entry.xml --- a/project_files/Android-build/SDL-android-project/res/layout/team_selection_entry.xml Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/res/layout/team_selection_entry.xml Sat Aug 18 00:47:51 2012 +0200 @@ -31,7 +31,7 @@ android:layout_alignBottom="@id/imgDifficulty" android:adjustViewBounds="true" android:scaleType="centerInside" - android:src="@drawable/teamcount7"/> + android:src="@drawable/hogcount"/> <TextView android:id="@+id/txtName" android:layout_height="fill_parent" diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/menu/main_options.xml --- a/project_files/Android-build/SDL-android-project/res/menu/main_options.xml Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/res/menu/main_options.xml Sat Aug 18 00:47:51 2012 +0200 @@ -9,4 +9,8 @@ android:title="@string/main_menu_preferences" android:icon="@android:drawable/ic_menu_preferences" android:showAsAction="ifRoom|withText" /> + <item + android:id="@+id/edit_weaponsets" + android:title="@string/edit_weaponsets_menu" + android:showAsAction="ifRoom|withText" /> </menu> \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml --- a/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml Sat Aug 18 00:47:51 2012 +0200 @@ -4,4 +4,8 @@ <item>@raw/team_one</item> <item>@raw/team_two</item> </array> +<array name="teamFilenames"> + <item>Team 1.hwt</item> + <item>Team 2.hwt</item> +</array> </resources> diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/res/values/strings.xml --- a/project_files/Android-build/SDL-android-project/res/values/strings.xml Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/res/values/strings.xml Sat Aug 18 00:47:51 2012 +0200 @@ -63,19 +63,49 @@ <string name="title_activity_lobby">Hedgewars Server Lobby</string> <string name="title_activity_room">Room</string> + <string name="title_activity_weaponset_list">User-defined Weaponsets</string> + <string name="title_activity_weaponset_creator">Weaponset Editor</string> + <string name="title_activity_scheme_list">User-defined Schemes</string> + <string name="title_activity_scheme_creator">Scheme Editor</string> <string name="chat_hint">Type here to chat</string> <!-- Map settings --> - <string name="map_map">Map</string> - <string name="map_type">Type</string> - <string name="map_seed">Change seed</string> + <string name="map_gen">Map</string> + <string name="map_name">Name</string> + <string name="map_template">Type</string> + <string name="map_maze_size">Type</string> + <string name="map_mission_prefix">Mission: </string> + <string-array name="map_types"> + <item>Generated map</item> + <item>Generated maze</item> + <item>Hand-drawn map</item> + <item>Map file</item> + </string-array> + <string-array name="map_templates"> + <item>Random</item> + <item>Small</item> + <item>Medium</item> + <item>Large</item> + <item>Cavern</item> + <item>Wacky</item> + </string-array> + <string-array name="map_maze_sizes"> + <item>Small tunnels</item> + <item>Medium tunnels</item> + <item>Large tunnels</item> + <item>Small floating islands</item> + <item>Medium floating islands</item> + <item>Large floating islands</item> + </string-array> <!-- Player list --> <string name="no_players_in_list">No players</string> <!-- Teamlist --> <string name="teamlist_addteam">Add team</string> + <string name="teamlist_color_button_description">Team color</string> + <string name="teamlist_hogcount_button_description">Hog count</string> <!-- Roomlist --> <string name="roomlist_header_roomname">Room Name</string> @@ -140,4 +170,11 @@ <string name="toast_disconnected">Disconnected: %1$s</string> <string name="toast_room_abandoned">The room was closed because the owner left.</string> <string name="toast_kicked">You were kicked from the room.</string> + + <!-- Weaponset editor --> + <string name="weaponsetlist_add_button_text">New Weaponset</string> + <string name="weaponsetlist_empty">No weaponsets</string> + <string name="edit_weaponsets_menu">Edit Weaponsets</string> + + <string name="schemelist_add_button_text">New Scheme</string> </resources> diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/ChatFragment.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/ChatFragment.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,78 @@ +package org.hedgewars.hedgeroid; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.netplay.MessageLog; +import org.hedgewars.hedgeroid.netplay.Netplay; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; + +public class ChatFragment extends Fragment { + public static final String ARGUMENT_INROOM = "inRoom"; + + private ChatlogAdapter adapter; + private Netplay netconn; + private MessageLog messageLog; + private boolean inRoom; + + public void setInRoom(boolean inRoom) { + this.inRoom = inRoom; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + netconn = Netplay.getAppInstance(getActivity().getApplicationContext()); + adapter = new ChatlogAdapter(getActivity()); + } + + @Override + public void onStart() { + super.onStart(); + messageLog = inRoom ? netconn.roomChatlog : netconn.lobbyChatlog; + adapter.setLog(messageLog.getLog()); + messageLog.registerObserver(adapter); + } + + @Override + public void onStop() { + super.onStop(); + messageLog.unregisterObserver(adapter); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_chat, container, false); + + ListView listView = (ListView) view.findViewById(R.id.chatConsole); + listView.setAdapter(adapter); + listView.setDivider(null); + listView.setDividerHeight(0); + listView.setVerticalFadingEdgeEnabled(true); + + EditText editText = (EditText) view.findViewById(R.id.chatInput); + editText.setOnEditorActionListener(new ChatSendListener()); + + return view; + } + + private final class ChatSendListener implements OnEditorActionListener { + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + String text = v.getText().toString(); + if(text.length()>0) { + v.setText(""); + netconn.sendChat(text); + } + return true; + } + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/ChatlogAdapter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/ChatlogAdapter.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,95 @@ +package org.hedgewars.hedgeroid; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.hedgewars.hedgeroid.netplay.MessageLog.Observer; + +import android.content.Context; +import android.text.method.LinkMovementMethod; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView.LayoutParams; +import android.widget.BaseAdapter; +import android.widget.TextView; + +/** + * Optimization: ListView is smart enough to try re-using the same view for an item + * with the same ID, but it still calls getView for those items when the list changes. + * Since lines with a given ID never change in our chatlog, we can avoid the effort + * of TextView.setText in many cases by checking if the view is already set up for the + * line with the right ID - but to do that, the view needs to remember the ID it's + * holding the text for. That's what the LoglineView does. + */ +class LoglineView extends TextView { + long chatlogId = -1; + + public LoglineView(Context context) { + super(context); + } +} + +public class ChatlogAdapter extends BaseAdapter implements Observer { + long idOffset = 0; + private List<CharSequence> log = new ArrayList<CharSequence>(); + private Context context; + + public ChatlogAdapter(Context context) { + this.context = context; + } + + public int getCount() { + return log.size(); + } + + public Object getItem(int position) { + return log.get(position); + } + + public long getItemId(int position) { + return position+idOffset; + } + + public boolean hasStableIds() { + return true; + } + + public void clear() { + idOffset += log.size(); + log.clear(); + notifyDataSetChanged(); + } + + public void lineAdded(CharSequence text) { + log.add(text); + notifyDataSetChanged(); + } + + public void lineRemoved() { + log.remove(0); + idOffset += 1; + notifyDataSetChanged(); + } + + public void setLog(Collection<CharSequence> log) { + idOffset += log.size(); + this.log = new ArrayList<CharSequence>(log); + notifyDataSetChanged(); + } + + public View getView(int position, View convertView, ViewGroup parent) { + LoglineView v = (LoglineView)convertView; + if (v == null) { + v = new LoglineView(context); + v.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); + v.setMovementMethod(LinkMovementMethod.getInstance()); + } + long id = getItemId(position); + if(id != v.chatlogId) { + v.setText(log.get(position)); + v.chatlogId = id; + } + return v; + } +} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/FrontendDataUtils.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/FrontendDataUtils.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/FrontendDataUtils.java Sat Aug 18 00:47:51 2012 +0200 @@ -22,12 +22,11 @@ import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.Utils; +import org.hedgewars.hedgeroid.util.FileUtils; import android.content.Context; import android.graphics.Bitmap; @@ -39,14 +38,13 @@ * @throws FileNotFoundException if the sdcard isn't available or the Maps directory doesn't exist */ public static ArrayList<MapFile> getMaps(Context c) throws FileNotFoundException { - File[] files = Utils.getFilesFromRelativeDir(c,"Maps"); + File[] files = FileUtils.getFilesFromRelativeDir(c,"Maps"); ArrayList<MapFile> ret = new ArrayList<MapFile>(); for(File f : files) { - boolean isMission = Utils.hasFileWithSuffix(f, ".lua"); + boolean isMission = FileUtils.hasFileWithSuffix(f, ".lua"); ret.add(new MapFile(f.getName(), isMission)); } - Collections.sort(ret, MapFile.MISSIONS_FIRST_NAME_ORDER); return ret; } @@ -56,7 +54,7 @@ * @throws FileNotFoundException if the sdcard isn't available or the Scripts/Multiplayer directory doesn't exist */ public static List<String> getGameStyles(Context c) throws FileNotFoundException { - File[] files = Utils.getFilesFromRelativeDir(c, "Scripts/Multiplayer"); + File[] files = FileUtils.getFilesFromRelativeDir(c, "Scripts/Multiplayer"); ArrayList<String> ret = new ArrayList<String>(); /* * Caution: It is important that the "empty" style has this exact name, because @@ -72,7 +70,6 @@ ret.add(name.replace('_', ' ').substring(0, name.length()-4)); } } - Collections.sort(ret, String.CASE_INSENSITIVE_ORDER); return ret; } @@ -80,24 +77,15 @@ * @throws FileNotFoundException if the sdcard isn't available or the Themes directory doesn't exist */ public static List<String> getThemes(Context c) throws FileNotFoundException { - List<String> list = Utils.getDirsWithFileSuffix(c, "Themes", "icon.png"); - Collections.sort(list, String.CASE_INSENSITIVE_ORDER); - return list; - } - - public static List<Weaponset> getWeaponsets(Context c) { - // TODO stub, re-implement - /*List<Weapon> list = Weapon.getWeapons(c); - Collections.sort(list);*/ - return Collections.emptyList(); + return FileUtils.getDirsWithFileSuffix(c, "Themes", "icon.png"); } /** * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Graves directory doesn't exist */ public static ArrayList<HashMap<String, ?>> getGraves(Context c) throws FileNotFoundException { - File gravePath = new File(new File(Utils.getDataPathFile(c), "Graphics"), "Graves"); - ArrayList<String> names = Utils.getFileNamesFromDirWithSuffix(c,"Graphics/Graves", ".png", true); + File gravePath = new File(new File(FileUtils.getDataPathFile(c), "Graphics"), "Graves"); + ArrayList<String> names = FileUtils.getFileNamesFromDirWithSuffix(c,"Graphics/Graves", ".png", true); ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(names.size()); for(String s : names){ @@ -123,8 +111,8 @@ * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Graves directory doesn't exist */ public static ArrayList<HashMap<String, ?>> getFlags(Context c) throws FileNotFoundException { - File flagsPath = new File(new File(Utils.getDataPathFile(c), "Graphics"), "Flags"); - ArrayList<String> names = Utils.getFileNamesFromDirWithSuffix(c, "Graphics/Flags", ".png", true); + File flagsPath = new File(new File(FileUtils.getDataPathFile(c), "Graphics"), "Flags"); + ArrayList<String> names = FileUtils.getFileNamesFromDirWithSuffix(c, "Graphics/Flags", ".png", true); ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(names.size()); for(String s : names){ @@ -141,7 +129,7 @@ * @throws FileNotFoundException if the sdcard isn't available or the Sounds/voices directory doesn't exist */ public static ArrayList<String> getVoices(Context c) throws FileNotFoundException { - File[] files = Utils.getFilesFromRelativeDir(c, "Sounds/voices"); + File[] files = FileUtils.getFilesFromRelativeDir(c, "Sounds/voices"); ArrayList<String> ret = new ArrayList<String>(); for(File f : files){ @@ -154,10 +142,9 @@ * @throws FileNotFoundException if the sdcard isn't available or the Forts directory doesn't exist */ public static ArrayList<String> getForts(Context c) throws FileNotFoundException { - return Utils.getFileNamesFromDirWithSuffix(c,"Forts", "L.png", true); + return FileUtils.getFileNamesFromDirWithSuffix(c,"Forts", "L.png", true); } - // TODO wat public static ArrayList<HashMap<String, ?>> getTypes(Context c){ ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(6); String[] levels = {c.getString(R.string.human), c.getString(R.string.bot5), c.getString(R.string.bot4), c.getString(R.string.bot3), c.getString(R.string.bot2), c.getString(R.string.bot1)}; @@ -167,6 +154,8 @@ HashMap<String, Object> map = new HashMap<String, Object>(); map.put("txt", levels[i]); map.put("img", images[i]); + map.put("level", i); + data.add(map); } @@ -177,8 +166,8 @@ * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Hats directory doesn't exist */ public static ArrayList<HashMap<String, ?>> getHats(Context c) throws FileNotFoundException { - ArrayList<String> files = Utils.getFileNamesFromDirWithSuffix(c,"Graphics/Hats", ".png", true); - File hatsPath = new File(new File(Utils.getDataPathFile(c), "Graphics"), "Hats"); + ArrayList<String> files = FileUtils.getFileNamesFromDirWithSuffix(c,"Graphics/Hats", ".png", true); + File hatsPath = new File(new File(FileUtils.getDataPathFile(c), "Graphics"), "Hats"); int size = files.size(); ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(size); @@ -202,9 +191,11 @@ File[] teamFileNames = teamsDir.listFiles(); if(teamFileNames != null){ for(File file : teamFileNames){ - Team team = Team.load(file); - if(team != null){ - ret.add(team); + if(file.getName().endsWith(".hwt")) { + Team team = Team.load(file); + if(team != null){ + ret.add(team); + } } } } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/GameConfig.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/GameConfig.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/GameConfig.java Sat Aug 18 00:47:51 2012 +0200 @@ -37,6 +37,7 @@ public static final String DEFAULT_STYLE = "Normal"; public static final String DEFAULT_SCHEME = "Default"; public static final String DEFAULT_WEAPONSET = "Default"; + public static final String DEFAULT_THEME = "Bamboo"; public final String style; public final Scheme scheme; @@ -51,4 +52,10 @@ this.teams = Collections.unmodifiableList(new ArrayList<TeamInGame>(teams)); this.weaponset = weaponset; } + + @Override + public String toString() { + return "GameConfig [style=" + style + ", scheme=" + scheme + ", map=" + + map + ", teams=" + teams + ", weaponset=" + weaponset + "]"; + } } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapFile.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapFile.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapFile.java Sat Aug 18 00:47:51 2012 +0200 @@ -2,18 +2,20 @@ import java.io.File; import java.io.FileNotFoundException; +import java.util.ArrayList; import java.util.Comparator; +import java.util.List; -import org.hedgewars.hedgeroid.Utils; +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.util.FileUtils; import android.content.Context; -import android.widget.AdapterView.OnItemSelectedListener; +import android.content.res.Resources; /** * Represents a map from the data directory */ public final class MapFile { - public static final String MISSION_PREFIX = "Mission: "; // TODO move text generation to UI to allow translation public static final String MAP_DIRECTORY = "Maps"; public final String name; @@ -28,7 +30,7 @@ * @throws FileNotFoundException if the sdcard is not available. Does NOT throw if the requested map file does not exist. */ public static File getFileForMapname(Context ctx, String mapname) throws FileNotFoundException { - return new File(new File(Utils.getDataPathFile(ctx), MAP_DIRECTORY), mapname); + return new File(new File(FileUtils.getDataPathFile(ctx), MAP_DIRECTORY), mapname); } public static final Comparator<MapFile> MISSIONS_FIRST_NAME_ORDER = new Comparator<MapFile>() { @@ -43,10 +45,23 @@ @Override public String toString() { - return (isMission ? MISSION_PREFIX : "") + name; + return "MapFile [name=" + name + ", isMission=" + isMission + "]"; } public File getPreviewFile(Context c) throws FileNotFoundException { - return new File(new File(new File(Utils.getDataPathFile(c), MAP_DIRECTORY), name), "preview.png"); - }; + return getPreviewFile(c, name); + } + + public static File getPreviewFile(Context c, String mapName) throws FileNotFoundException { + return new File(FileUtils.getDataPathFile(c), MAP_DIRECTORY + "/" + mapName + "/" + "preview.png"); + } + + public static List<String> toDisplayNameList(List<MapFile> mapFiles, Resources res) { + String missionPrefix = res.getString(R.string.map_mission_prefix); + List<String> result = new ArrayList<String>(); + for(MapFile mapFile : mapFiles) { + result.add((mapFile.isMission ? missionPrefix : "") + mapFile.name); + } + return result; + } } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapRecipe.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapRecipe.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapRecipe.java Sat Aug 18 00:47:51 2012 +0200 @@ -18,6 +18,9 @@ package org.hedgewars.hedgeroid.Datastructures; +import java.util.Arrays; +import java.util.UUID; + import org.hedgewars.hedgeroid.R; import org.hedgewars.hedgeroid.frontlib.Frontlib; @@ -65,6 +68,34 @@ return drawData==null ? null : drawData.clone(); } + public MapRecipe withMapgen(int mapgen) { + return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData); + } + + public MapRecipe withTemplateFilter(int templateFilter) { + return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData); + } + + public MapRecipe withMazeSize(int mazeSize) { + return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData); + } + + public MapRecipe withName(String name) { + return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData); + } + + public MapRecipe withSeed(String seed) { + return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData); + } + + public MapRecipe withTheme(String theme) { + return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData); + } + + public MapRecipe withDrawData(byte[] drawData) { + return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData); + } + public static String formatMapName(Resources res, String map) { if(map.charAt(0)=='+') { if(map.equals(MAPNAME_REGULAR)) { @@ -77,4 +108,95 @@ } return map; } + + /** + * Returns the mapname corresponding to the map generator (e.g. "+rnd+" for regular maps) + * If the mapgen does not have a unique name (MAPGEN_NAMED) or is not known, the def + * value is returned. + */ + public static String mapnameForGenerator(int mapgen, String def) { + switch(mapgen) { + case Frontlib.MAPGEN_REGULAR: return MAPNAME_REGULAR; + case Frontlib.MAPGEN_MAZE: return MAPNAME_MAZE; + case Frontlib.MAPGEN_DRAWN: return MAPNAME_DRAWN; + default: return def; + } + } + + /** + * In a sense this is the inverse of mapnameForGenerator. Returns the mapgen that uses + * mapName as special identifier, or MAPGEN_NAMED if there is none. + */ + public static int generatorForMapname(String mapName) { + if(MapRecipe.MAPNAME_REGULAR.equals(mapName)) { + return Frontlib.MAPGEN_REGULAR; + } else if(MapRecipe.MAPNAME_MAZE.equals(mapName)) { + return Frontlib.MAPGEN_MAZE; + } else if(MapRecipe.MAPNAME_DRAWN.equals(mapName)) { + return Frontlib.MAPGEN_DRAWN; + } else { + return Frontlib.MAPGEN_NAMED; + } + } + + @Override + public String toString() { + return "MapRecipe [mapgen=" + mapgen + ", templateFilter=" + + templateFilter + ", mazeSize=" + mazeSize + ", name=" + name + + ", seed=" + seed + ", theme=" + theme + ", drawData=" + + Arrays.toString(drawData) + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(drawData); + result = prime * result + mapgen; + result = prime * result + mazeSize; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((seed == null) ? 0 : seed.hashCode()); + result = prime * result + templateFilter; + result = prime * result + ((theme == null) ? 0 : theme.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + MapRecipe other = (MapRecipe) obj; + if (!Arrays.equals(drawData, other.drawData)) + return false; + if (mapgen != other.mapgen) + return false; + if (mazeSize != other.mazeSize) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (seed == null) { + if (other.seed != null) + return false; + } else if (!seed.equals(other.seed)) + return false; + if (templateFilter != other.templateFilter) + return false; + if (theme == null) { + if (other.theme != null) + return false; + } else if (!theme.equals(other.theme)) + return false; + return true; + } + + public static String makeRandomSeed() { + return "{"+UUID.randomUUID().toString()+"}"; + } } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MetaScheme.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MetaScheme.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MetaScheme.java Sat Aug 18 00:47:51 2012 +0200 @@ -4,7 +4,11 @@ import java.util.Collections; import java.util.List; +import org.hedgewars.hedgeroid.frontlib.Flib; + public final class MetaScheme { + public static final MetaScheme INSTANCE = Flib.INSTANCE.flib_get_metascheme().deref(); + public static final class Mod { public final String name; public final int bitmaskIndex; diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Player.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Player.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Player.java Sat Aug 18 00:47:51 2012 +0200 @@ -1,14 +1,29 @@ package org.hedgewars.hedgeroid.Datastructures; +import java.util.Comparator; + +/** + * Basic information about a player on a server. + */ public final class Player { public final String name; + public final boolean registered, admin; - public Player(String name) { + public Player(String name, boolean registered, boolean admin) { this.name = name; + this.registered = registered; + this.admin = admin; } @Override public String toString() { - return "Player [name=" + name + "]"; + return "Player [name=" + name + ", registered=" + registered + + ", admin=" + admin + "]"; } + + public static Comparator<Player> NAME_ORDER = new Comparator<Player>() { + public int compare(Player lhs, Player rhs) { + return lhs.name.compareToIgnoreCase(rhs.name); + } + }; } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/PlayerInRoom.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/PlayerInRoom.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,16 @@ +package org.hedgewars.hedgeroid.Datastructures; + +public final class PlayerInRoom { + public final Player player; + public final boolean ready; + + public PlayerInRoom(Player player, boolean ready) { + this.player = player; + this.ready = ready; + } + + @Override + public String toString() { + return "PlayerInRoom [player=" + player + ", ready=" + ready + "]"; + } +} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Room.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Room.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,36 @@ +package org.hedgewars.hedgeroid.Datastructures; + +import android.content.res.Resources; + +/** + * A room as presented in the roomlist in a server lobby. + */ +public final class Room { + public final String name, map, scheme, weapons, owner; + public final int playerCount, teamCount; + public final boolean inProgress; + + public Room(String name, String map, String scheme, String weapons, + String owner, int playerCount, int teamCount, boolean inProgress) { + this.name = name; + this.map = map; + this.scheme = scheme; + this.weapons = weapons; + this.owner = owner; + this.playerCount = playerCount; + this.teamCount = teamCount; + this.inProgress = inProgress; + } + + public String formatMapName(Resources res) { + return MapRecipe.formatMapName(res, map); + } + + @Override + public String toString() { + return "RoomlistRoom [name=" + name + ", map=" + map + ", scheme=" + + scheme + ", weapons=" + weapons + ", owner=" + owner + + ", playerCount=" + playerCount + ", teamCount=" + teamCount + + ", inProgress=" + inProgress + "]"; + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/RoomWithId.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/RoomWithId.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,24 @@ +package org.hedgewars.hedgeroid.Datastructures; + +import java.util.Comparator; + +public final class RoomWithId { + public final Room room; + public final long id; + + public RoomWithId(Room room, long id) { + this.room = room; + this.id = id; + } + + @Override + public String toString() { + return "RoomWithId [room=" + room + ", id=" + id + "]"; + } + + public static final Comparator<RoomWithId> NEWEST_FIRST_ORDER = new Comparator<RoomWithId>() { + public int compare(RoomWithId lhs, RoomWithId rhs) { + return rhs.id<lhs.id ? -1 : rhs.id>lhs.id ? 1 : 0; + } + }; +} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/RoomlistRoom.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/RoomlistRoom.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -package org.hedgewars.hedgeroid.Datastructures; - -import android.content.res.Resources; - -public final class RoomlistRoom { - public final String name, map, scheme, weapons, owner; - public final int playerCount, teamCount; - public final boolean inProgress; - - public RoomlistRoom(String name, String map, String scheme, String weapons, - String owner, int playerCount, int teamCount, boolean inProgress) { - this.name = name; - this.map = map; - this.scheme = scheme; - this.weapons = weapons; - this.owner = owner; - this.playerCount = playerCount; - this.teamCount = teamCount; - this.inProgress = inProgress; - } - - public String formatMapName(Resources res) { - return MapRecipe.formatMapName(res, map); - } - - @Override - public String toString() { - return "RoomlistRoom [name=" + name + ", map=" + map + ", scheme=" - + scheme + ", weapons=" + weapons + ", owner=" + owner - + ", playerCount=" + playerCount + ", teamCount=" + teamCount - + ", inProgress=" + inProgress + "]"; - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java Sat Aug 18 00:47:51 2012 +0200 @@ -23,15 +23,14 @@ import java.util.Comparator; import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; public final class Scheme { - public final MetaScheme metascheme; public final String name; public final Map<String, Integer> settings; public final Map<String, Boolean> mods; - public Scheme(MetaScheme metascheme, String name, Map<String, Integer> settings, Map<String, Boolean> mods) { - this.metascheme = metascheme; + public Scheme(String name, Map<String, Integer> settings, Map<String, Boolean> mods) { this.name = name; this.settings = Collections.unmodifiableMap(new HashMap<String, Integer>(settings)); this.mods = Collections.unmodifiableMap(new HashMap<String, Boolean>(mods)); @@ -42,20 +41,66 @@ return health==null ? 100 : health.intValue(); } - /*@Override - public String toString() { - return "Scheme [metascheme=" + metascheme + ", name=" + name - + ", settings=" + settings + ", mods=" + mods + "]"; - }*/ + public static Scheme createDefaultScheme(MetaScheme meta) { + String name = GameConfig.DEFAULT_SCHEME; + Map<String, Integer> settings = new TreeMap<String, Integer>(); + Map<String, Boolean> mods = new TreeMap<String, Boolean>(); + for(MetaScheme.Setting setting : meta.settings) { + settings.put(setting.name, setting.def); + } + for(MetaScheme.Mod mod : meta.mods) { + mods.put(mod.name, Boolean.FALSE); + } + return new Scheme(name, settings, mods); + } @Override public String toString() { - return name; // TODO change back once StartGameActivity does not need this anymore + return "Scheme [name=" + name + ", settings=" + settings + ", mods=" + + mods + "]"; } - public static final Comparator<Scheme> caseInsensitiveNameComparator = new Comparator<Scheme>() { + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((mods == null) ? 0 : mods.hashCode()); + result = prime * result + + ((settings == null) ? 0 : settings.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Scheme other = (Scheme) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (mods == null) { + if (other.mods != null) + return false; + } else if (!mods.equals(other.mods)) + return false; + if (settings == null) { + if (other.settings != null) + return false; + } else if (!settings.equals(other.settings)) + return false; + return true; + } + + public static final Comparator<Scheme> NAME_ORDER = new Comparator<Scheme>() { public int compare(Scheme lhs, Scheme rhs) { - return lhs.name.compareToIgnoreCase(rhs.name); + return String.CASE_INSENSITIVE_ORDER.compare(lhs.name, rhs.name); } }; } \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Schemes.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Schemes.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Schemes.java Sat Aug 18 00:47:51 2012 +0200 @@ -3,14 +3,9 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import org.hedgewars.hedgeroid.Utils; import org.hedgewars.hedgeroid.frontlib.Flib; -import org.hedgewars.hedgeroid.frontlib.Frontlib.MetaschemePtr; import org.hedgewars.hedgeroid.frontlib.Frontlib.SchemelistPtr; import android.content.Context; @@ -32,57 +27,49 @@ return new File(c.getFilesDir(), "schemes_builtin.ini"); } - public static Map<String, Scheme> loadAllSchemes(Context c) throws IOException { - Map<String, Scheme> result = loadUserSchemes(c); - result.putAll(loadBuiltinSchemes(c)); + public static List<Scheme> loadAllSchemes(Context c) throws IOException { + List<Scheme> result = loadBuiltinSchemes(c); + result.addAll(loadUserSchemes(c)); return result; } - public static Map<String, Scheme> loadUserSchemes(Context c) throws IOException { + public static List<Scheme> loadUserSchemes(Context c) throws IOException { return loadSchemes(c, getUserSchemesFile(c)); } - public static Map<String, Scheme> loadBuiltinSchemes(Context c) throws IOException { + public static List<Scheme> loadBuiltinSchemes(Context c) throws IOException { return loadSchemes(c, getBuiltinSchemesFile(c)); } - public static Map<String, Scheme> loadSchemes(Context c, File schemeFile) throws IOException { - Map<String, Scheme> result = new TreeMap<String, Scheme>(); - String metaschemePath = new File(Utils.getDataPathFile(c), "metasettings.ini").getAbsolutePath(); + public static List<Scheme> loadSchemes(Context c, File schemeFile) throws IOException { if(!schemeFile.isFile()) { // No schemes file == no schemes, no error - return new TreeMap<String, Scheme>(); + return new ArrayList<Scheme>(); } - MetaschemePtr meta = null; SchemelistPtr schemeListPtr = null; try { - meta = Flib.INSTANCE.flib_metascheme_from_ini(metaschemePath); - if(meta==null) { - throw new IOException("Unable to read metascheme"); - } - schemeListPtr = Flib.INSTANCE.flib_schemelist_from_ini(meta, schemeFile.getAbsolutePath()); + schemeListPtr = Flib.INSTANCE.flib_schemelist_from_ini(schemeFile.getAbsolutePath()); if(schemeListPtr == null) { throw new IOException("Unable to read schemelist"); } - List<Scheme> schemeList = schemeListPtr.deref(); - for(Scheme scheme : schemeList) { - result.put(scheme.name, scheme); - } - return result; + return schemeListPtr.deref(); } finally { if(schemeListPtr != null) { Flib.INSTANCE.flib_schemelist_destroy(schemeListPtr); } - if(meta != null) { - Flib.INSTANCE.flib_metascheme_release(meta); - } } } - public static void saveUserSchemes(Context c, Map<String, Scheme> schemes) throws IOException { - List<Scheme> schemeList = new ArrayList<Scheme>(schemes.values()); - Collections.sort(schemeList, Scheme.caseInsensitiveNameComparator); - SchemelistPtr ptr = SchemelistPtr.createJavaOwned(schemeList); + public static void saveUserSchemes(Context c, List<Scheme> schemes) throws IOException { + SchemelistPtr ptr = SchemelistPtr.createJavaOwned(schemes); Flib.INSTANCE.flib_schemelist_to_ini(getUserSchemesFile(c).getAbsolutePath(), ptr); } + + public static List<String> toNameList(List<Scheme> schemes) { + List<String> result = new ArrayList<String>(); + for(Scheme scheme : schemes) { + result.add(scheme.name); + } + return result; + } } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java Sat Aug 18 00:47:51 2012 +0200 @@ -25,10 +25,10 @@ import java.util.Comparator; import java.util.List; -import org.hedgewars.hedgeroid.Utils; import org.hedgewars.hedgeroid.EngineProtocol.PascalExports; import org.hedgewars.hedgeroid.frontlib.Flib; import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr; +import org.hedgewars.hedgeroid.util.FileUtils; import android.content.Context; @@ -72,7 +72,7 @@ } public static File getTeamfileByName(Context c, String teamName) { - return new File(new File(c.getFilesDir(), DIRECTORY_TEAMS), Utils.replaceBadChars(teamName)+".hwt"); + return new File(new File(c.getFilesDir(), DIRECTORY_TEAMS), FileUtils.replaceBadChars(teamName)+".hwt"); } @Override diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamInGame.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamInGame.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamInGame.java Sat Aug 18 00:47:51 2012 +0200 @@ -1,5 +1,8 @@ package org.hedgewars.hedgeroid.Datastructures; +import java.util.Collection; +import java.util.Comparator; + /** * A team with per-game configuration. This is similar to the frontlib "team" structure, * except that it does not include weaponset and initial health, which are handled on a @@ -17,4 +20,20 @@ public TeamInGame withAttribs(TeamIngameAttributes attribs) { return new TeamInGame(team, attribs); } + + public static int getUnusedOrRandomColorIndex(Collection<TeamInGame> teams) { + int[] illegalColors = new int[teams.size()]; + int i=0; + for(TeamInGame team : teams) { + illegalColors[i] = team.ingameAttribs.colorIndex; + i++; + } + return TeamIngameAttributes.randomColorIndex(illegalColors); + } + + public static Comparator<TeamInGame> NAME_ORDER = new Comparator<TeamInGame>() { + public int compare(TeamInGame lhs, TeamInGame rhs) { + return Team.NAME_ORDER.compare(lhs.team, rhs.team); + } + }; } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamIngameAttributes.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamIngameAttributes.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamIngameAttributes.java Sat Aug 18 00:47:51 2012 +0200 @@ -28,7 +28,7 @@ this.remoteDriven = remoteDriven; } - public static int randomColorIndex(int[] illegalColors){ + public static int randomColorIndex(int[] illegalColors) { Random rnd = new Random(); ArrayList<Integer> legalcolors = new ArrayList<Integer>(); for(int i=0; i<TEAM_COLORS.length; i++) { diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponset.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponset.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponset.java Sat Aug 18 00:47:51 2012 +0200 @@ -1,5 +1,7 @@ package org.hedgewars.hedgeroid.Datastructures; +import java.util.Comparator; + import org.hedgewars.hedgeroid.frontlib.Flib; public final class Weaponset { @@ -17,6 +19,65 @@ @Override public String toString() { - return name; // TODO use the generated one once StartGameActivity doesn't need this anymore + return "Weaponset [name=" + name + ", loadout=" + loadout + + ", crateProb=" + crateProb + ", crateAmmo=" + crateAmmo + + ", delay=" + delay + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((crateAmmo == null) ? 0 : crateAmmo.hashCode()); + result = prime * result + + ((crateProb == null) ? 0 : crateProb.hashCode()); + result = prime * result + ((delay == null) ? 0 : delay.hashCode()); + result = prime * result + ((loadout == null) ? 0 : loadout.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Weaponset other = (Weaponset) obj; + if (crateAmmo == null) { + if (other.crateAmmo != null) + return false; + } else if (!crateAmmo.equals(other.crateAmmo)) + return false; + if (crateProb == null) { + if (other.crateProb != null) + return false; + } else if (!crateProb.equals(other.crateProb)) + return false; + if (delay == null) { + if (other.delay != null) + return false; + } else if (!delay.equals(other.delay)) + return false; + if (loadout == null) { + if (other.loadout != null) + return false; + } else if (!loadout.equals(other.loadout)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } + + public static Comparator<Weaponset> NAME_ORDER = new Comparator<Weaponset>() { + public int compare(Weaponset lhs, Weaponset rhs) { + return String.CASE_INSENSITIVE_ORDER.compare(lhs.name, rhs.name); + } + }; } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponsets.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponsets.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponsets.java Sat Aug 18 00:47:51 2012 +0200 @@ -3,6 +3,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import org.hedgewars.hedgeroid.frontlib.Flib; @@ -60,4 +61,24 @@ WeaponsetListPtr ptr = WeaponsetListPtr.createJavaOwned(weaponsets); Flib.INSTANCE.flib_weaponsetlist_to_ini(getUserWeaponsetsFile(c).getAbsolutePath(), ptr); } + + public static void deleteUserWeaponset(Context c, String setToDelete) throws IOException { + List<Weaponset> userWeaponsets = loadUserWeaponsets(c); + for(Iterator<Weaponset> iter = userWeaponsets.iterator(); iter.hasNext();) { + Weaponset set = iter.next(); + if(set.name.equals(setToDelete)) { + iter.remove(); + break; + } + } + saveUserWeaponsets(c, userWeaponsets); + } + + public static List<String> toNameList(List<Weaponset> weaponsets) { + List<String> result = new ArrayList<String>(); + for(Weaponset weaponset : weaponsets) { + result.add(weaponset.name); + } + return result; + } } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAssets.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAssets.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAssets.java Sat Aug 18 00:47:51 2012 +0200 @@ -6,10 +6,10 @@ import org.hedgewars.hedgeroid.MainActivity; import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.Utils; import org.hedgewars.hedgeroid.Datastructures.Schemes; import org.hedgewars.hedgeroid.Datastructures.Team; import org.hedgewars.hedgeroid.Datastructures.Weaponsets; +import org.hedgewars.hedgeroid.util.FileUtils; import android.content.res.AssetManager; import android.os.AsyncTask; @@ -25,7 +25,7 @@ private void copyFileOrDir(AssetManager assetManager, File target, String assetPath) throws IOException { try { - Utils.writeStreamToFile(assetManager.open(assetPath), target); + FileUtils.writeStreamToFile(assetManager.open(assetPath), target); } catch(FileNotFoundException e) { /* * I can't find a better way to figure out whether an asset entry is @@ -44,11 +44,11 @@ @Override protected Boolean doInBackground(Object... params) { try { - Utils.writeStreamToFile(act.getResources().openRawResource(R.raw.schemes_builtin), Schemes.getBuiltinSchemesFile(act)); - Utils.writeStreamToFile(act.getResources().openRawResource(R.raw.weapons_builtin), Weaponsets.getBuiltinWeaponsetsFile(act)); - Utils.resRawToFilesDir(act, R.array.teams, Team.DIRECTORY_TEAMS); - copyFileOrDir(act.getAssets(), Utils.getDataPathFile(act), "Data"); - copyFileOrDir(act.getAssets(), new File(Utils.getCachePath(act), VERSION_FILENAME), VERSION_FILENAME); + FileUtils.writeStreamToFile(act.getResources().openRawResource(R.raw.schemes_builtin), Schemes.getBuiltinSchemesFile(act)); + FileUtils.writeStreamToFile(act.getResources().openRawResource(R.raw.weapons_builtin), Weaponsets.getBuiltinWeaponsetsFile(act)); + FileUtils.resRawToFilesDir(act, R.array.teams, R.array.teamFilenames, Team.DIRECTORY_TEAMS); + copyFileOrDir(act.getAssets(), FileUtils.getDataPathFile(act), "Data"); + copyFileOrDir(act.getAssets(), new File(FileUtils.getCachePath(act), VERSION_FILENAME), VERSION_FILENAME); return Boolean.TRUE; } catch(IOException e) { Log.e("DownloadAssets", e.getMessage(), e); diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadPackage.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadPackage.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadPackage.java Sat Aug 18 00:47:51 2012 +0200 @@ -20,7 +20,7 @@ import java.io.IOException; -import org.hedgewars.hedgeroid.Utils; +import org.hedgewars.hedgeroid.util.FileUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -136,7 +136,7 @@ version = -1; } }else if(name.equals("path")){ - path = Utils.getDataPath(c) + text; + path = FileUtils.getDataPath(c) + text; }else if(name.equals("representation")){ representation = text; }else if(name.equals("description")){ diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/PascalExports.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/PascalExports.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/PascalExports.java Sat Aug 18 00:47:51 2012 +0200 @@ -32,5 +32,8 @@ } public static native int HWgetMaxNumberOfTeams(); - public static native int HWterminate(boolean b); + public static native int HWterminate(boolean b); + public static native int HWGenLandPreview(int port); + + public static Object engineMutex = new Object(); } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/GameConnection.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/GameConnection.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,214 @@ +package org.hedgewars.hedgeroid; + +import org.hedgewars.hedgeroid.Datastructures.GameConfig; +import org.hedgewars.hedgeroid.frontlib.Flib; +import org.hedgewars.hedgeroid.frontlib.Frontlib; +import org.hedgewars.hedgeroid.frontlib.Frontlib.BytesCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.GameSetupPtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.GameconnPtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.IntCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.VoidCallback; +import org.hedgewars.hedgeroid.netplay.GameMessageListener; +import org.hedgewars.hedgeroid.netplay.Netplay; +import org.hedgewars.hedgeroid.util.TickHandler; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.util.Log; + +import com.sun.jna.Memory; +import com.sun.jna.NativeLong; +import com.sun.jna.Pointer; + +public final class GameConnection { + private static final Handler mainHandler = new Handler(Looper.getMainLooper()); + + private final HandlerThread thread; + private final Handler handler; + private final TickHandler tickHandler; + private final Netplay netplay; // ==null if not a netgame + private GameconnPtr conn; + + /** + * The actual connection has to be set up on a separate thread because networking + * is not allowed on the UI thread, so the port can't be queried immediately after + * creating the GameConnection object. Instead, one of these interface methods is + * called once we know which port we are listening on (or once we fail to set this up). + * Methods will be called on the UI thread. + */ + public static interface Listener { + /** + * We are listening for the engine at $port, go start the engine. + */ + void gameConnectionReady(int port); + + /** + * The connection has stopped, either because the game has ended or was interrupted, + * or maybe we failed to create the connection at all (in that case gameConnectionReady wasn't called). + */ + void gameConnectionDisconnected(int reason); + } + + private GameConnection(Netplay netplay) { + this.netplay = netplay; + thread = new HandlerThread("IPCThread"); + thread.start(); + handler = new Handler(thread.getLooper()); + tickHandler = new TickHandler(thread.getLooper(), 50, new Runnable() { + public void run() { + if(conn != null) { + Flib.INSTANCE.flib_gameconn_tick(conn); + } + } + }); + tickHandler.start(); + } + + public static GameConnection forNetgame(final GameConfig config, Netplay netplay, final Listener listener) { + final GameConnection result = new GameConnection(netplay); + final String playerName = netplay.getPlayerName(); + result.handler.post(new Runnable() { + public void run() { + GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create(playerName, GameSetupPtr.createJavaOwned(config), true); + result.setupConnection(conn, true, listener); + } + }); + return result; + } + + public static GameConnection forLocalGame(final GameConfig config, final Listener listener) { + final GameConnection result = new GameConnection(null); + result.handler.post(new Runnable() { + public void run() { + GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create("Player", GameSetupPtr.createJavaOwned(config), false); + result.setupConnection(conn, false, listener); + } + }); + return result; + } + + // runs on the IPCThread + private void setupConnection(GameconnPtr conn, final boolean netgame, final Listener listener) { + if(conn == null) { + mainHandler.post(new Runnable() { + public void run() { listener.gameConnectionDisconnected(Frontlib.GAME_END_ERROR); } + }); + shutdown(); + } else { + this.conn = conn; + final int port = Flib.INSTANCE.flib_gameconn_getport(conn); + mainHandler.post(new Runnable() { + public void run() { + listener.gameConnectionReady(port); + if(netgame) { + netplay.registerGameMessageListener(gameMessageListener); + } + } + }); + Flib.INSTANCE.flib_gameconn_onConnect(conn, connectCb, null); + Flib.INSTANCE.flib_gameconn_onDisconnect(conn, disconnectCb, null); + Flib.INSTANCE.flib_gameconn_onErrorMessage(conn, errorMessageCb, null); + if(netgame) { + Flib.INSTANCE.flib_gameconn_onChat(conn, chatCb, null); + Flib.INSTANCE.flib_gameconn_onEngineMessage(conn, engineMessageCb, null); + } + } + } + + // runs on the IPCThread + private void shutdown() { + tickHandler.stop(); + thread.quit(); + Flib.INSTANCE.flib_gameconn_destroy(conn); + if(netplay != null) { + mainHandler.post(new Runnable() { + public void run() { + netplay.unregisterGameMessageListener(gameMessageListener); + } + }); + } + } + + // runs on the IPCThread + private final StrBoolCallback chatCb = new StrBoolCallback() { + public void callback(Pointer context, String message, boolean teamChat) { + if(teamChat) { + netplay.sendTeamChat(message); + } else { + netplay.sendChat(message); + } + } + }; + + // runs on the IPCThread + private final VoidCallback connectCb = new VoidCallback() { + public void callback(Pointer context) { + Log.i("GameConnection", "Connected"); + } + }; + + // runs on the IPCThread + private final IntCallback disconnectCb = new IntCallback() { + public void callback(Pointer context, int reason) { + if(netplay != null) { + netplay.sendRoundFinished(reason==Frontlib.GAME_END_FINISHED); + } + shutdown(); + } + }; + + // runs on the IPCThread + private final BytesCallback engineMessageCb = new BytesCallback() { + public void callback(Pointer context, Pointer buffer, NativeLong size) { + netplay.sendEngineMessage(buffer.getByteArray(0, size.intValue())); + } + }; + + // runs on the IPCThread + private final StrCallback errorMessageCb = new StrCallback() { + public void callback(Pointer context, String message) { + Log.e("GameConnection", message); + } + }; + + // runs on any thread + private final GameMessageListener gameMessageListener = new GameMessageListener() { + public void onNetDisconnected() { + handler.post(new Runnable() { + public void run() { + shutdown(); + } + }); + } + + public void onMessage(final int type, final String message) { + handler.post(new Runnable() { + public void run() { + Flib.INSTANCE.flib_gameconn_send_textmsg(conn, type, message); + } + }); + } + + public void onEngineMessage(final byte[] em) { + handler.post(new Runnable() { + public void run() { + Memory mem = new Memory(em.length); + mem.write(0, em, 0, em.length); + Flib.INSTANCE.flib_gameconn_send_enginemsg(conn, mem, new NativeLong(em.length)); + } + }); + + } + + public void onChatMessage(final String nick, final String message) { + handler.post(new Runnable() { + public void run() { + Flib.INSTANCE.flib_gameconn_send_chatmsg(conn, nick, message); + } + }); + } + }; +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyActivity.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyActivity.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,140 @@ +package org.hedgewars.hedgeroid; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.NetplayStateFragment.NetplayStateListener; +import org.hedgewars.hedgeroid.netplay.Netplay; +import org.hedgewars.hedgeroid.netplay.Netplay.State; +import org.hedgewars.hedgeroid.util.TextInputDialog; +import org.hedgewars.hedgeroid.util.TextInputDialog.TextInputDialogListener; + +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TabHost; +import android.widget.TextView; + +public class LobbyActivity extends FragmentActivity implements TextInputDialogListener, NetplayStateListener { + private static final int DIALOG_CREATE_ROOM = 0; + + private TabHost tabHost; + private Netplay netplay; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + setContentView(R.layout.activity_lobby); + ChatFragment chatFragment = (ChatFragment)getSupportFragmentManager().findFragmentById(R.id.chatFragment); + chatFragment.setInRoom(false); + + FragmentTransaction trans = getSupportFragmentManager().beginTransaction(); + trans.add(new NetplayStateFragment(), "netplayFragment"); + trans.commit(); + + netplay = Netplay.getAppInstance(getApplicationContext()); + + tabHost = (TabHost)findViewById(android.R.id.tabhost); + if(tabHost != null) { + tabHost.setup(); + tabHost.getTabWidget().setOrientation(LinearLayout.VERTICAL); + + tabHost.addTab(tabHost.newTabSpec("rooms").setIndicator(createIndicatorView(tabHost, R.string.lobby_tab_rooms, getResources().getDrawable(R.drawable.roomlist_ingame))).setContent(R.id.roomListFragment)); + tabHost.addTab(tabHost.newTabSpec("chat").setIndicator(createIndicatorView(tabHost, R.string.lobby_tab_chat, getResources().getDrawable(R.drawable.edit))).setContent(R.id.chatFragment)); + tabHost.addTab(tabHost.newTabSpec("players").setIndicator(createIndicatorView(tabHost, R.string.lobby_tab_players, getResources().getDrawable(R.drawable.human))).setContent(R.id.playerListFragment)); + + if (icicle != null) { + tabHost.setCurrentTabByTag(icicle.getString("currentTab")); + } + } + } + + private View createIndicatorView(TabHost tabHost, int label, Drawable icon) { + LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + View tabIndicator = inflater.inflate(R.layout.tab_indicator, + tabHost.getTabWidget(), // tab widget is the parent + false); // no inflate params + + final TextView tv = (TextView) tabIndicator.findViewById(R.id.title); + tv.setText(label); + + if(icon != null) { + final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon); + iconView.setImageDrawable(icon); + } + + return tabIndicator; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.lobby_options, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case R.id.room_create: + TextInputDialog dialog = new TextInputDialog(DIALOG_CREATE_ROOM, R.string.dialog_create_room_title, 0, R.string.dialog_create_room_hint); + dialog.show(getSupportFragmentManager(), "create_room_dialog"); + return true; + case R.id.disconnect: + netplay.disconnect(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onBackPressed() { + super.onBackPressed(); + netplay.disconnect(); + } + + @Override + protected void onSaveInstanceState(Bundle icicle) { + super.onSaveInstanceState(icicle); + if(tabHost != null) { + icicle.putString("currentTab", tabHost.getCurrentTabTag()); + } + } + + public void onTextInputDialogSubmitted(int dialogId, String text) { + if(text != null && text.length()>0) { + netplay.sendCreateRoom(text); + } + } + + public void onTextInputDialogCancelled(int dialogId) { + } + + public void onNetplayStateChanged(State newState) { + switch(newState) { + case CONNECTING: + case NOT_CONNECTED: + finish(); + break; + case ROOM: + case INGAME: + startActivity(new Intent(getApplicationContext(), RoomActivity.class)); + break; + case LOBBY: + // Do nothing + break; + default: + throw new IllegalStateException("Unknown connection state: "+newState); + } + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyPlayerlistAdapter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyPlayerlistAdapter.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,32 @@ +package org.hedgewars.hedgeroid; + +import java.util.Comparator; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.Player; +import org.hedgewars.hedgeroid.util.ObservableTreeMapAdapter; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class LobbyPlayerlistAdapter extends ObservableTreeMapAdapter<String, Player> { + @Override + protected Comparator<Player> getEntryOrder() { + return Player.NAME_ORDER; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View v = convertView; + if (v == null) { + LayoutInflater vi = LayoutInflater.from(parent.getContext()); + v = vi.inflate(R.layout.listview_player, null); + } + + String player = getItem(position).name; + TextView username = (TextView) v.findViewById(android.R.id.text1); + username.setText(player); + return v; + } +} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyPlayerlistFragment.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyPlayerlistFragment.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,75 @@ +package org.hedgewars.hedgeroid; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.Player; +import org.hedgewars.hedgeroid.netplay.Netplay; + +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView.AdapterContextMenuInfo; + +public class LobbyPlayerlistFragment extends ListFragment { + private Netplay netplay; + private LobbyPlayerlistAdapter adapter; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + netplay = Netplay.getAppInstance(getActivity().getApplicationContext()); + adapter = new LobbyPlayerlistAdapter(); + adapter.setSource(netplay.lobbyPlayerlist); + setListAdapter(adapter); + } + + @Override + public void onDestroy() { + super.onDestroy(); + adapter.invalidate(); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + registerForContextMenu(getListView()); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo; + MenuInflater inflater = getActivity().getMenuInflater(); + inflater.inflate(R.menu.lobby_playerlist_context, menu); + menu.setHeaderIcon(R.drawable.human); + menu.setHeaderTitle(adapter.getItem(info.position).name); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo(); + Player player = adapter.getItem(info.position); + switch(item.getItemId()) { + case R.id.player_info: + netplay.sendPlayerInfoQuery(player.name); + return true; + case R.id.player_follow: + netplay.sendFollowPlayer(player.name); + return true; + default: + return super.onContextItemSelected(item); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_playerlist, container, false); + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java Sat Aug 18 00:47:51 2012 +0200 @@ -25,9 +25,9 @@ import org.hedgewars.hedgeroid.Downloader.DownloadAssets; import org.hedgewars.hedgeroid.Downloader.DownloadListActivity; import org.hedgewars.hedgeroid.frontlib.Flib; -import org.hedgewars.hedgeroid.netplay.LobbyActivity; import org.hedgewars.hedgeroid.netplay.Netplay; import org.hedgewars.hedgeroid.netplay.Netplay.State; +import org.hedgewars.hedgeroid.util.FileUtils; import android.app.AlertDialog; import android.app.Dialog; @@ -65,19 +65,19 @@ startLocalGame.setOnClickListener(startGameListener); startNetGame.setOnClickListener(startNetGameListener); - if(!Utils.isDataPathAvailable()){ + if(!FileUtils.isDataPathAvailable()){ showDialog(DIALOG_NO_SDCARD); } else { String existingVersion = ""; try { - File versionFile = new File(Utils.getCachePath(this), "assetsversion.txt"); - existingVersion = Utils.readToString(new FileInputStream(versionFile)); + File versionFile = new File(FileUtils.getCachePath(this), "assetsversion.txt"); + existingVersion = FileUtils.readToString(new FileInputStream(versionFile)); } catch(IOException e) { } String newVersion = ""; try { - newVersion = Utils.readToString(getAssets().open("assetsversion.txt")); + newVersion = FileUtils.readToString(getAssets().open("assetsversion.txt")); } catch(IOException e) { } @@ -125,23 +125,14 @@ case R.id.preferences: Toast.makeText(this, R.string.not_implemented_yet, Toast.LENGTH_SHORT).show(); return true; + case R.id.edit_weaponsets: + startActivity(new Intent(getApplicationContext(), WeaponsetListActivity.class)); + return true; default: return super.onOptionsItemSelected(item); } } - @Override - protected void onStart() { - super.onStart(); - Netplay.getAppInstance(getApplicationContext()).requestFastTicks(); - } - - @Override - protected void onStop() { - super.onStop(); - Netplay.getAppInstance(getApplicationContext()).unrequestFastTicks(); - } - public Dialog onCreateDialog(int id, Bundle args){ switch(id) { case DIALOG_NO_SDCARD: diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MapFragment.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MapFragment.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,279 @@ +package org.hedgewars.hedgeroid; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils; +import org.hedgewars.hedgeroid.Datastructures.MapFile; +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; +import org.hedgewars.hedgeroid.Datastructures.Scheme; +import org.hedgewars.hedgeroid.Datastructures.Weaponset; +import org.hedgewars.hedgeroid.frontlib.Frontlib; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.Spinner; +import android.widget.TableRow; +import android.widget.Toast; + +public class MapFragment extends Fragment implements RoomStateManager.Observer { + private Spinner mapTypeSpinner, mapNameSpinner, templateSpinner, mazeSizeSpinner; + private TableRow nameRow, templateRow, mazeSizeRow; + private ImageView mapPreview; + private List<MapFile> mapFiles; + private RoomStateManager stateManager; + private Random random = new Random(); + private CalmDownHandler mapPreviewHandler; + + /* + * Rendering the preview can take a few seconds on Android, so we want to prevent preview + * requests from queueing up if maps are changed quickly. So if there is already a preview + * being generated, we store our latest request in the newPreviewRequest variable instead. + * Once the current preview is finished generating it will start on that one. + */ + private boolean previewGenerationInProgress; + private MapRecipe newPreviewRequest; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_map, container, false); + + final Context appContext = getActivity().getApplicationContext(); + mapPreviewHandler = new CalmDownHandler(getActivity().getMainLooper(), new Runnable() { + public void run() { + if(!previewGenerationInProgress) { + mapPreview.setImageResource(R.drawable.roomlist_preparing); + MapPreviewGenerator.startPreviewGeneration(appContext, stateManager.getMapRecipe(), mapPreviewListener); + previewGenerationInProgress = true; + } else { + newPreviewRequest = stateManager.getMapRecipe(); + } + } + }, 250); + + nameRow = (TableRow) v.findViewById(R.id.rowMapName); + templateRow = (TableRow) v.findViewById(R.id.rowTemplateFilter); + mazeSizeRow = (TableRow) v.findViewById(R.id.rowMazeSize); + mapPreview = (ImageView) v.findViewById(R.id.mapPreview); + mapPreview.setImageDrawable(null);; + mapPreview.setOnClickListener(mapClickListener); + + try { + mapFiles = FrontendDataUtils.getMaps(getActivity()); + } catch (IOException e) { + Toast.makeText(getActivity().getApplicationContext(), R.string.error_missing_sdcard_or_files, Toast.LENGTH_LONG).show(); + getActivity().finish(); + } + Collections.sort(mapFiles, MapFile.MISSIONS_FIRST_NAME_ORDER); + + List<String> mapNames = MapFile.toDisplayNameList(mapFiles, getResources()); + mapTypeSpinner = prepareSpinner(v, R.id.spinMapType, Arrays.asList(getResources().getStringArray(R.array.map_types)), mapTypeSelectedListener); + mapNameSpinner = prepareSpinner(v, R.id.spinMapName, mapNames, mapNameSelectedListener); + templateSpinner = prepareSpinner(v, R.id.spinTemplateFilter, Arrays.asList(getResources().getStringArray(R.array.map_templates)), mapTemplateSelectedListener); + mazeSizeSpinner = prepareSpinner(v, R.id.spinMazeSize, Arrays.asList(getResources().getStringArray(R.array.map_maze_sizes)), mazeSizeSelectedListener); + + stateManager.registerObserver(this); + MapRecipe map = stateManager.getMapRecipe(); + if(map != null) { + updateDisplay(map); + } + setChiefState(stateManager.getChiefStatus()); + mapPreviewHandler.activity(); + return v; + } + + private static Spinner prepareSpinner(View v, int id, List<String> items, OnItemSelectedListener itemSelectedListener) { + Spinner spinner = (Spinner)v.findViewById(id); + ArrayAdapter<String> adapter = new ArrayAdapter<String>(v.getContext(), R.layout.listview_item, items); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + spinner.setOnItemSelectedListener(itemSelectedListener); + return spinner; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + try { + stateManager = ((RoomStateManager.Provider)getActivity()).getRoomStateManager(); + } catch(ClassCastException e) { + throw new RuntimeException("Hosting activity must implement RoomStateManager.Provider.", e); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapPreviewHandler.stop(); + stateManager.unregisterObserver(this); + } + + private void setChiefState(boolean chiefState) { + mapTypeSpinner.setEnabled(chiefState); + mapNameSpinner.setEnabled(chiefState); + templateSpinner.setEnabled(chiefState); + mazeSizeSpinner.setEnabled(chiefState); + mapPreview.setEnabled(chiefState); + + if(chiefState) { + sendMapnameAndGenerator(); + stateManager.changeMapTemplate(templateSpinner.getSelectedItemPosition()); + stateManager.changeMazeSize(mazeSizeSpinner.getSelectedItemPosition()); + } + } + + private void updateDisplay(MapRecipe map) { + nameRow.setVisibility(map.mapgen == Frontlib.MAPGEN_NAMED ? View.VISIBLE : View.GONE); + templateRow.setVisibility(map.mapgen == Frontlib.MAPGEN_REGULAR ? View.VISIBLE : View.GONE); + mazeSizeRow.setVisibility(map.mapgen == Frontlib.MAPGEN_MAZE ? View.VISIBLE : View.GONE); + + mapTypeSpinner.setSelection(map.mapgen); + int mapPosition = findMapPosition(mapFiles, map.name); + if(mapPosition >= 0) { + mapNameSpinner.setSelection(mapPosition); + } + templateSpinner.setSelection(map.templateFilter); + mazeSizeSpinner.setSelection(map.mazeSize); + } + + private static int findMapPosition(List<MapFile> mapFiles, String mapName) { + for(int i=0; i<mapFiles.size(); i++) { + if(mapName.equals(mapFiles.get(i).name)) { + return i; + } + } + return -1; + } + + private void sendMapnameAndGenerator() { + int mapType = mapTypeSpinner.getSelectedItemPosition(); + String mapName = mapFiles.get(mapNameSpinner.getSelectedItemPosition()).name; + stateManager.changeMapNameAndGenerator(MapRecipe.mapnameForGenerator(mapType, mapName)); + } + + private final OnItemSelectedListener mapTypeSelectedListener = new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) { + sendMapnameAndGenerator(); + } + public void onNothingSelected(AdapterView<?> arg0) {} + }; + + private final OnItemSelectedListener mapNameSelectedListener = new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) { + sendMapnameAndGenerator(); + } + public void onNothingSelected(AdapterView<?> arg0) {} + }; + + private final OnItemSelectedListener mapTemplateSelectedListener = new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) { + stateManager.changeMapTemplate(position); + } + public void onNothingSelected(AdapterView<?> arg0) {} + }; + + private final OnItemSelectedListener mazeSizeSelectedListener = new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) { + stateManager.changeMazeSize(position); + } + public void onNothingSelected(AdapterView<?> arg0) {} + }; + + private final OnClickListener mapClickListener = new OnClickListener() { + public void onClick(View v) { + stateManager.changeMapSeed(MapRecipe.makeRandomSeed()); + switch(mapTypeSpinner.getSelectedItemPosition()) { + case Frontlib.MAPGEN_NAMED: + mapNameSpinner.setSelection(random.nextInt(mapNameSpinner.getCount())); + break; + case Frontlib.MAPGEN_REGULAR: + templateSpinner.setSelection(Frontlib.TEMPLATEFILTER_ALL); + break; + case Frontlib.MAPGEN_MAZE: + mazeSizeSpinner.setSelection(random.nextInt(mazeSizeSpinner.getCount())); + break; + } + } + }; + + public void onChiefStatusChanged(boolean isChief) { + setChiefState(isChief); + } + + public void onMapChanged(MapRecipe recipe) { + updateDisplay(recipe); + mapPreviewHandler.activity(); + } + + public void onGameStyleChanged(String gameStyle) { } + public void onSchemeChanged(Scheme scheme) { } + public void onWeaponsetChanged(Weaponset weaponset) { } + + private MapPreviewGenerator.Listener mapPreviewListener = new MapPreviewGenerator.Listener() { + public void onMapPreviewResult(Drawable preview) { + if(newPreviewRequest != null) { + MapPreviewGenerator.startPreviewGeneration(getActivity().getApplicationContext(), newPreviewRequest, mapPreviewListener); + newPreviewRequest = null; + } else { + if(mapPreview != null) { + mapPreview.setImageDrawable(preview); + } + previewGenerationInProgress = false; + } + } + }; + + /** + * This class allows you to define a runnable that is called when there has been no activity + * for a set amount of time, where activity is determined by calls to the activity() method + * of the handler. It is used here to update the map preview when there have been no updates + * to the relevant map information for a time, to prevent triggering several updates at once + * when different parts of the information change. + */ + private static final class CalmDownHandler extends Handler { + int runningMessagesCounter = 0; + final Runnable inactivityRunnable; + final long inactivityMs; + boolean stopped; + + public CalmDownHandler(Looper looper, Runnable runnable, long inactivityMs) { + super(looper); + this.inactivityRunnable = runnable; + this.inactivityMs = inactivityMs; + } + + public void activity() { + runningMessagesCounter++; + sendMessageDelayed(obtainMessage(), inactivityMs); + } + + @Override + public void handleMessage(Message msg) { + runningMessagesCounter--; + if(runningMessagesCounter==0 && !stopped) { + inactivityRunnable.run(); + } + } + + public void stop() { + stopped = true; + } + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MapPreviewGenerator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MapPreviewGenerator.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,200 @@ +package org.hedgewars.hedgeroid; + +import java.io.File; +import java.io.FileNotFoundException; + +import org.hedgewars.hedgeroid.Datastructures.MapFile; +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; +import org.hedgewars.hedgeroid.EngineProtocol.PascalExports; +import org.hedgewars.hedgeroid.frontlib.Flib; +import org.hedgewars.hedgeroid.frontlib.Frontlib; +import org.hedgewars.hedgeroid.frontlib.Frontlib.MapRecipePtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.MapconnPtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.MapimageCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback; +import org.hedgewars.hedgeroid.util.FileUtils; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import com.sun.jna.Pointer; + +/** + * A class that asynchronously generates a map preview from a MapRecipe. + * + * For named maps, this will load the preview image from the filesystem. For others, + * it will call the engine to generate a preview image. + */ +public final class MapPreviewGenerator implements Runnable { + private static final String TAG = MapPreviewGenerator.class.getSimpleName(); + private static final Handler mainHandler = new Handler(Looper.getMainLooper()); + + private final Context appContext; + private final MapRecipe map; + private final Listener listener; + + private boolean resultAvailable; + private Drawable result; + + public static interface Listener { + /** + * This is called on the UI thread once the preview is ready or failed. + * In case of failure, null is passed. + */ + void onMapPreviewResult(Drawable preview); + } + + private MapPreviewGenerator(Context appContext, MapRecipe map, Listener listener) { + this.appContext = appContext; + this.map = map; + this.listener = listener; + } + + public void run() { + if (map.mapgen == Frontlib.MAPGEN_NAMED) { + postToListener(loadPreviewFromFile(appContext, map.name)); + } else { + resultAvailable = false; + result = null; + MapconnPtr conn = Flib.INSTANCE.flib_mapconn_create(MapRecipePtr.createJavaOwned(map)); + if (conn == null) { + postToListener(null); + return; + } + try { + int port = Flib.INSTANCE.flib_mapconn_getport(conn); + Flib.INSTANCE.flib_mapconn_onSuccess(conn, successCb, null); + Flib.INSTANCE.flib_mapconn_onFailure(conn, failureCb, null); + + String configPath; + try { + configPath = FileUtils.getCachePath(appContext).getAbsolutePath(); + } catch(FileNotFoundException e) { + return; + } + + startEngine(configPath, port); + long startTime = System.nanoTime(); + do { + Flib.INSTANCE.flib_mapconn_tick(conn); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + // ignore + } + } while(!resultAvailable && System.nanoTime()-startTime < 15000000000l); // 15 seconds timeout + } finally { + Flib.INSTANCE.flib_mapconn_destroy(conn); + postToListener(result); + } + } + } + + public static void startPreviewGeneration(Context appContext, MapRecipe map, Listener listener) { + new Thread(new MapPreviewGenerator(appContext, map, listener)).start(); + } + + private static Drawable loadPreviewFromFile(Context appContext, String mapName) { + if(!mapName.startsWith("+")) { + try { + File previewFile = MapFile.getPreviewFile(appContext, mapName); + return Drawable.createFromPath(previewFile.getAbsolutePath()); + } catch (FileNotFoundException e) { + Log.w("MapPreviewGenerator", "Preview for map "+mapName+" not found."); + } + } + return null; + } + + private static void startEngine(final String configPath, final int port) { + new Thread(new Runnable() { + public void run() { + Log.d(TAG, "Starting engine "+port); + synchronized(PascalExports.engineMutex) { + PascalExports.HWGenLandPreview(port); + } + Log.d(TAG, "Engine finished"); + } + }).start(); + } + + private void postToListener(final Drawable result) { + mainHandler.post(new Runnable() { + public void run() { + listener.onMapPreviewResult(result); + } + }); + } + + /** + * Let's be extra nice here and clip off the left and right sides, so the preview is centered... + * Since the image is present in bytes, we can save some effort by checking entire byte-columns first. + */ + private final MapimageCallback successCb = new MapimageCallback() { + public void callback(Pointer context, Pointer buffer, int hedgehogCount) { + Log.d(TAG, "Running success handler"); + byte[] mapdata = buffer.getByteArray(0, Frontlib.MAPIMAGE_BYTES); + + int leftmostPixel = Frontlib.MAPIMAGE_WIDTH; + int rightmostPixel = -1; + int bytesPerLine = Frontlib.MAPIMAGE_WIDTH/8; + + // Find the leftmost pixel + for(int xbyte=0; xbyte<bytesPerLine; xbyte++) { + for(int y=0; y<Frontlib.MAPIMAGE_HEIGHT; y++) { + int b = 0xff&mapdata[xbyte+y*bytesPerLine]; + if(b != 0) { + leftmostPixel = Math.min(leftmostPixel, Integer.numberOfLeadingZeros(b)-24+xbyte*8); + } + } + if(leftmostPixel!=Frontlib.MAPIMAGE_WIDTH) break; + } + + // Find the rightmost pixel + for(int xbyte=bytesPerLine-1; xbyte>=0; xbyte--) { + for(int y=0; y<Frontlib.MAPIMAGE_HEIGHT; y++) { + int b = mapdata[xbyte+y*bytesPerLine]; + if(b != 0) { + rightmostPixel = Math.max(rightmostPixel, xbyte*8+7-Integer.numberOfTrailingZeros(b)); + } + } + if(rightmostPixel!=-1) break; + } + + // No pixel was set at all -> use default width + if(rightmostPixel==-1) { + leftmostPixel = 0; + rightmostPixel = Frontlib.MAPIMAGE_WIDTH-1; + } + + Bitmap bitmap = Bitmap.createBitmap(rightmostPixel-leftmostPixel+1, Frontlib.MAPIMAGE_HEIGHT, Config.ARGB_8888); + for(int y=0; y<Frontlib.MAPIMAGE_HEIGHT; y++) { + for(int x=0; x<bitmap.getWidth(); x++) { + bitmap.setPixel(x, y, isPixelSet(mapdata, x+leftmostPixel, y) ? Color.YELLOW : Color.TRANSPARENT); + } + } + result = new BitmapDrawable(bitmap); + resultAvailable = true; + } + }; + + private static boolean isPixelSet(byte[] imgdata, int x, int y) { + int pixelnum = x+Frontlib.MAPIMAGE_WIDTH*y; + return (imgdata[pixelnum>>3] & (128>>(pixelnum&7))) != 0; + } + + private final StrCallback failureCb = new StrCallback() { + public void callback(Pointer context, String reason) { + Log.e(TAG, "Error generating map preview: "+reason); + result = null; + resultAvailable = true; + } + }; +} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/NetplayStateFragment.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/NetplayStateFragment.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,120 @@ +package org.hedgewars.hedgeroid; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.frontlib.Frontlib; +import org.hedgewars.hedgeroid.netplay.Netplay; +import org.hedgewars.hedgeroid.netplay.Netplay.State; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.content.LocalBroadcastManager; +import android.widget.Toast; + +/** + * Fragment for use by an activity that depends on the state of the network + * connection. The activity must implement the NetplayStateListener interface. + * + * This fragment manages a few aspects of the netplay connection: Requesting + * the network system loop to run at high frequency while the activity is in + * the foreground, and reacting to changes in the networking state by calling + * a callback method on the activity. + */ +public class NetplayStateFragment extends Fragment { + private Netplay netplay; + private Context appContext; + private LocalBroadcastManager broadcastManager; + private NetplayStateListener listener; + private State knownState; + + interface NetplayStateListener { + /** + * This is called while the activity is running, and every time during resume, if + * a change in the networking state is detected. It is also called once + * with the initial state (which could be called a change from the "unknown" state). + */ + void onNetplayStateChanged(State newState); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + listener = (NetplayStateListener) activity; + } catch(ClassCastException e) { + throw new ClassCastException("Activity " + activity + " must implement NetplayStateListener to use NetplayStateFragment."); + } + } + + @Override + public void onDetach() { + super.onDetach(); + listener = null; + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + appContext = getActivity().getApplicationContext(); + broadcastManager = LocalBroadcastManager.getInstance(appContext); + netplay = Netplay.getAppInstance(appContext); + } + + @Override + public void onResume() { + super.onResume(); + broadcastManager.registerReceiver(disconnectReceiver, new IntentFilter(Netplay.ACTION_DISCONNECTED)); + broadcastManager.registerReceiver(leaveRoomReceiver, new IntentFilter(Netplay.ACTION_LEFT_ROOM)); + broadcastManager.registerReceiver(stateChangeReceiver, new IntentFilter(Netplay.ACTION_STATE_CHANGED)); + + State newState = netplay.getState(); + if(knownState != newState) { + listener.onNetplayStateChanged(newState); + knownState = newState; + } + } + + @Override + public void onPause() { + super.onPause(); + broadcastManager.unregisterReceiver(disconnectReceiver); + broadcastManager.unregisterReceiver(leaveRoomReceiver); + broadcastManager.unregisterReceiver(stateChangeReceiver); + } + + private final BroadcastReceiver disconnectReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if(intent.getBooleanExtra(Netplay.EXTRA_HAS_ERROR, true)) { + String message = intent.getStringExtra(Netplay.EXTRA_MESSAGE); + String toastText = getString(R.string.toast_disconnected, message); + Toast.makeText(appContext, toastText, Toast.LENGTH_LONG).show(); + } + } + }; + + private final BroadcastReceiver leaveRoomReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int reason = intent.getIntExtra(Netplay.EXTRA_REASON, -1); + if(reason == Frontlib.NETCONN_ROOMLEAVE_ABANDONED) { + Toast.makeText(appContext, R.string.toast_room_abandoned, Toast.LENGTH_LONG).show(); + } else if(reason == Frontlib.NETCONN_ROOMLEAVE_KICKED) { + Toast.makeText(appContext, R.string.toast_kicked, Toast.LENGTH_LONG).show(); + } + } + }; + + private final BroadcastReceiver stateChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + State newState = netplay.getState(); + listener.onNetplayStateChanged(newState); + knownState = newState; + } + }; +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomActivity.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomActivity.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,85 @@ +package org.hedgewars.hedgeroid; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.Team; +import org.hedgewars.hedgeroid.NetplayStateFragment.NetplayStateListener; +import org.hedgewars.hedgeroid.netplay.Netplay; +import org.hedgewars.hedgeroid.netplay.Netplay.State; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.widget.TabHost; +import android.widget.Toast; + +public class RoomActivity extends FragmentActivity implements NetplayStateListener, TeamAddDialog.Listener, RoomStateManager.Provider { + private TabHost tabHost; + private Netplay netplay; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + netplay = Netplay.getAppInstance(getApplicationContext()); + + setContentView(R.layout.activity_netroom); + ChatFragment chatFragment = (ChatFragment)getSupportFragmentManager().findFragmentById(R.id.chatFragment); + chatFragment.setInRoom(true); + + FragmentTransaction trans = getSupportFragmentManager().beginTransaction(); + trans.add(new NetplayStateFragment(), "netplayFragment"); + trans.commit(); + + /*tabHost = (TabHost)findViewById(android.R.id.tabhost); + if(tabHost != null) { + tabHost.setup(); + tabHost.getTabWidget().setOrientation(LinearLayout.VERTICAL); + + //tabHost.addTab(tabHost.newTabSpec("chat").setIndicator(createIndicatorView(tabHost, R.string.lobby_tab_chat, getResources().getDrawable(R.drawable.edit))).setContent(R.id.chatFragment)); + //tabHost.addTab(tabHost.newTabSpec("players").setIndicator(createIndicatorView(tabHost, R.string.lobby_tab_players, getResources().getDrawable(R.drawable.human))).setContent(R.id.playerListFragment)); + + if (icicle != null) { + tabHost.setCurrentTabByTag(icicle.getString("currentTab")); + } + }*/ + } + + @Override + public void onBackPressed() { + netplay.sendLeaveRoom(null); + } + + @Override + protected void onSaveInstanceState(Bundle icicle) { + super.onSaveInstanceState(icicle); + if(tabHost != null) { + icicle.putString("currentTab", tabHost.getCurrentTabTag()); + } + } + + public void onNetplayStateChanged(State newState) { + switch(newState) { + case NOT_CONNECTED: + case CONNECTING: + case LOBBY: + finish(); + break; + case ROOM: + // Do nothing + break; + case INGAME: + //startActivity(new Intent(getApplicationContext(), RoomActivity.class)); + Toast.makeText(getApplicationContext(), R.string.not_implemented_yet, Toast.LENGTH_SHORT).show(); + break; + default: + throw new IllegalStateException("Unknown connection state: "+newState); + } + } + + public void onTeamAddDialogSubmitted(Team newTeam) { + netplay.sendAddTeam(newTeam); + } + + public RoomStateManager getRoomStateManager() { + return netplay.getRoomStateManager(); + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomPlayerlistAdapter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomPlayerlistAdapter.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,41 @@ +package org.hedgewars.hedgeroid; + +import java.util.Comparator; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.PlayerInRoom; +import org.hedgewars.hedgeroid.util.ObservableTreeMapAdapter; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class RoomPlayerlistAdapter extends ObservableTreeMapAdapter<String, PlayerInRoom> { + @Override + protected Comparator<PlayerInRoom> getEntryOrder() { + return AlphabeticalOrderComparator.INSTANCE; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View v = convertView; + if (v == null) { + LayoutInflater vi = LayoutInflater.from(parent.getContext()); + v = vi.inflate(R.layout.listview_player, null); + } + + PlayerInRoom player = getItem(position); + TextView username = (TextView) v.findViewById(android.R.id.text1); + username.setText(player.player.name); + int readyDrawable = player.ready ? R.drawable.lightbulb_on : R.drawable.lightbulb_off; + username.setCompoundDrawablesWithIntrinsicBounds(readyDrawable, 0, 0, 0); + return v; + } + + private static final class AlphabeticalOrderComparator implements Comparator<PlayerInRoom> { + public static final AlphabeticalOrderComparator INSTANCE = new AlphabeticalOrderComparator(); + public int compare(PlayerInRoom lhs, PlayerInRoom rhs) { + return lhs.player.name.compareToIgnoreCase(rhs.player.name); + }; + } +} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomPlayerlistFragment.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomPlayerlistFragment.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,103 @@ +package org.hedgewars.hedgeroid; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.GameConfig; +import org.hedgewars.hedgeroid.Datastructures.Player; +import org.hedgewars.hedgeroid.Datastructures.PlayerInRoom; +import org.hedgewars.hedgeroid.netplay.Netplay; +import org.hedgewars.hedgeroid.netplay.RunGameListener; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.AdapterView.OnItemClickListener; + +public class RoomPlayerlistFragment extends ListFragment implements OnItemClickListener, RunGameListener { + private Netplay netplay; + private RoomPlayerlistAdapter adapter; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + netplay = Netplay.getAppInstance(getActivity().getApplicationContext()); + netplay.registerRunGameListener(this); + adapter = new RoomPlayerlistAdapter(); + adapter.setSource(netplay.roomPlayerlist); + setListAdapter(adapter); + } + + @Override + public void onDestroy() { + super.onDestroy(); + adapter.invalidate(); + netplay.unregisterRunGameListener(this); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + registerForContextMenu(getListView()); + getListView().setOnItemClickListener(this); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo; + String playerName = adapter.getItem(info.position).player.name; + + MenuInflater inflater = getActivity().getMenuInflater(); + inflater.inflate(R.menu.room_playerlist_context, menu); + if(netplay.isChief() && !playerName.equals(netplay.getPlayerName())) { + inflater.inflate(R.menu.room_playerlist_chief_context, menu); + } + menu.setHeaderIcon(R.drawable.human); + menu.setHeaderTitle(playerName); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo(); + PlayerInRoom player = adapter.getItem(info.position); + switch(item.getItemId()) { + case R.id.player_info: + netplay.sendPlayerInfoQuery(player.player.name); + return true; + case R.id.player_kick: + netplay.sendKick(player.player.name); + return true; + default: + return super.onContextItemSelected(item); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_playerlist, container, false); + } + + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + Player player = adapter.getItem(position).player; + if(player.name.equals(netplay.getPlayerName())) { + netplay.sendToggleReady(); + } + } + + // TODO this is really the wrong place for this... + public void runGame(GameConfig config) { + SDLActivity.startConfig = config; + SDLActivity.startNetgame = true; + startActivity(new Intent(getActivity().getApplicationContext(), SDLActivity.class)); + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomStateManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomStateManager.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,70 @@ +package org.hedgewars.hedgeroid; + +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; +import org.hedgewars.hedgeroid.Datastructures.Scheme; +import org.hedgewars.hedgeroid.Datastructures.Weaponset; + +/** + * This interface is supposed to abstract the handling of room state for several fragments + * that can display and manipulate it. The purpose of this is to allow using these fragments + * both for setting up networked and local games, despite the fact that for local games + * the settings can be changed immediately in memory, while they have to be sent out to the + * server for networked games. + * + * If/when the state changes as result of calling one of the "changeX" functions, that will + * also trigger the corresponding change listener method. There is no guarantee that calling + * a changeX method will actually change the setting (e.g. if you're not room chief). + * + * For local games, getChiefStatus is always true. + * + * Implementations of this interface are probably not thread safe and should only be used on + * the UI thread. + */ +public interface RoomStateManager { + // Query current state + MapRecipe getMapRecipe(); + boolean getChiefStatus(); + Scheme getScheme(); + String getGameStyle(); + Weaponset getWeaponset(); + + // Manipulate state + void changeMapRecipe(MapRecipe map); + void changeMapTheme(String theme); + + /** + * This function sets both the map's name and generator. There is no function + * to change them independendly since e.g. the QtFrontend relies on them being + * consistent. + * + * If the name parameter is equal to one of the MapRecipe.MAPNAME_REGULAR, MAPNAME_MAZE + * or MAPNAME_DRAWN constants, the map generator is set accordingly. Otherwise, the + * map generator is set to represent a mapfile. The map's name is always set to + * the parameter. + */ + void changeMapNameAndGenerator(String mapName); + void changeMapTemplate(int template); + void changeMazeSize(int mazeSize); + void changeMapSeed(String seed); + void changeMapDrawdata(byte[] drawdata); + + void changeScheme(Scheme scheme); + void changeGameStyle(String style); + void changeWeaponset(Weaponset weaponset); + + // Observe state + void registerObserver(Observer observer); + void unregisterObserver(Observer observer); + + public interface Observer { + void onMapChanged(MapRecipe recipe); + void onChiefStatusChanged(boolean isChief); + void onSchemeChanged(Scheme scheme); + void onGameStyleChanged(String gameStyle); + void onWeaponsetChanged(Weaponset weaponset); + } + + public interface Provider { + RoomStateManager getRoomStateManager(); + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomlistAdapter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomlistAdapter.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,91 @@ +package org.hedgewars.hedgeroid; + +import java.util.Comparator; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.Room; +import org.hedgewars.hedgeroid.Datastructures.RoomWithId; +import org.hedgewars.hedgeroid.util.ObservableTreeMapAdapter; + +import android.content.Context; +import android.content.res.Resources; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class RoomlistAdapter extends ObservableTreeMapAdapter<String, RoomWithId> { + private Context context; + + public RoomlistAdapter(Context context) { + this.context = context; + } + + @Override + protected Comparator<RoomWithId> getEntryOrder() { + return RoomWithId.NEWEST_FIRST_ORDER; + } + + @Override + public long getItemId(int position) { + return getItem(position).id; + } + + @Override + public boolean hasStableIds() { + return true; + } + + private static CharSequence formatExtra(Resources res, Room room) { + String ownermsg = res.getString(R.string.roomlist_owner, room.owner); + String mapmsg = res.getString(R.string.roomlist_map, room.formatMapName(res)); + String scheme = room.scheme.equals(room.weapons) ? room.scheme : room.scheme + " / " + room.weapons; + String schememsg = res.getString(R.string.roomlist_scheme, scheme); + return ownermsg + ". " + mapmsg + ", " + schememsg; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View v = convertView; + if (v == null) { + LayoutInflater vi = LayoutInflater.from(context); + v = vi.inflate(R.layout.listview_room, null); + } + + Room room = getItem(position).room; + int iconRes = room.inProgress ? R.drawable.roomlist_ingame : R.drawable.roomlist_preparing; + + if(v.findViewById(android.R.id.text1) == null) { + // Tabular room list + TextView roomnameView = (TextView)v.findViewById(R.id.roomname); + TextView playerCountView = (TextView)v.findViewById(R.id.playercount); + TextView teamCountView = (TextView)v.findViewById(R.id.teamcount); + TextView ownerView = (TextView)v.findViewById(R.id.owner); + TextView mapView = (TextView)v.findViewById(R.id.map); + TextView schemeView = (TextView)v.findViewById(R.id.scheme); + TextView weaponView = (TextView)v.findViewById(R.id.weapons); + + roomnameView.setCompoundDrawablesWithIntrinsicBounds(iconRes, 0, 0, 0); + roomnameView.setText(room.name); + if(playerCountView != null) { + playerCountView.setText(String.valueOf(room.playerCount)); + } + if(teamCountView != null) { + teamCountView.setText(String.valueOf(room.teamCount)); + } + ownerView.setText(room.owner); + mapView.setText(room.formatMapName(context.getResources())); + schemeView.setText(room.scheme); + weaponView.setText(room.weapons); + } else { + // Small room list + TextView v1 = (TextView)v.findViewById(android.R.id.text1); + TextView v2 = (TextView)v.findViewById(android.R.id.text2); + + v1.setCompoundDrawablesWithIntrinsicBounds(iconRes, 0, 0, 0); + v1.setText(room.name); + v2.setText(formatExtra(context.getResources(), room)); + } + + return v; + } +} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomlistFragment.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomlistFragment.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,72 @@ +package org.hedgewars.hedgeroid; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.netplay.Netplay; + +import android.os.Bundle; +import android.os.CountDownTimer; +import android.support.v4.app.ListFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; + +public class RoomlistFragment extends ListFragment implements OnItemClickListener { + private static final int AUTO_REFRESH_INTERVAL_MS = 15000; + + private Netplay netplay; + private RoomlistAdapter adapter; + private CountDownTimer autoRefreshTimer = new CountDownTimer(Long.MAX_VALUE, AUTO_REFRESH_INTERVAL_MS) { + @Override + public void onTick(long millisUntilFinished) { + netplay.sendRoomlistRequest(); + } + + @Override + public void onFinish() { } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + netplay = Netplay.getAppInstance(getActivity().getApplicationContext()); + adapter = new RoomlistAdapter(getActivity()); + adapter.setSource(netplay.roomList); + setListAdapter(adapter); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.lobby_rooms_fragment, container, false); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + getListView().setOnItemClickListener(this); + } + + @Override + public void onResume() { + super.onResume(); + autoRefreshTimer.start(); + } + + @Override + public void onPause() { + super.onPause(); + autoRefreshTimer.cancel(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + adapter.invalidate(); + } + + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + netplay.sendJoinRoom(adapter.getItem(position).room.name); + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java Sat Aug 18 00:47:51 2012 +0200 @@ -1,5 +1,8 @@ package org.hedgewars.hedgeroid; +import java.io.UnsupportedEncodingException; +import java.util.concurrent.atomic.AtomicBoolean; + import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; @@ -12,7 +15,9 @@ import org.hedgewars.hedgeroid.frontlib.Frontlib.GameSetupPtr; import org.hedgewars.hedgeroid.frontlib.Frontlib.GameconnPtr; import org.hedgewars.hedgeroid.frontlib.Frontlib.IntCallback; -import org.hedgewars.hedgeroid.netplay.TickHandler; +import org.hedgewars.hedgeroid.netplay.Netplay; +import org.hedgewars.hedgeroid.util.FileUtils; +import org.hedgewars.hedgeroid.util.TickHandler; import com.sun.jna.Pointer; @@ -31,6 +36,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Message; +import android.util.Base64; import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; @@ -50,6 +56,7 @@ * way to do this (http://developer.android.com/guide/faq/framework.html#3) */ public static volatile GameConfig startConfig; + public static volatile boolean startNetgame; // Main components public static SDLActivity mSingleton; @@ -87,7 +94,7 @@ mSingleton = this; // Set up the surface - mSurface = new SDLSurface(getApplication(), startConfig); + mSurface = new SDLSurface(getApplication(), startConfig, startNetgame); setContentView(mSurface); } @@ -150,8 +157,14 @@ commandHandler.sendMessage(msg); } + public static void synchronizedNativeInit(String...args) { + synchronized(PascalExports.engineMutex) { + nativeInit(args); + } + } + // C functions we call - public static native void nativeInit(String...args); + private static native void nativeInit(String...args); public static native void nativeQuit(); public static native void nativePause(); public static native void nativeResume(); @@ -184,12 +197,29 @@ return mSingleton; } - public static void startApp(int width, int height, GameConfig config) { + public static void startApp(final int width, final int height, GameConfig config, boolean netgame) { synchronized(SDLActivity.class) { - // Start up the C app thread + // Start up the C app thread TODO this is silly code if (mSDLThread == null) { - mSDLThread = new Thread(new SDLMain(width, height, config), "SDLThread"); - mSDLThread.start(); + final AtomicBoolean gameconnStartDone = new AtomicBoolean(false); + GameConnection.Listener listener = new GameConnection.Listener() { + public void gameConnectionReady(int port) { + mSDLThread = new Thread(new SDLMain(width, height, port, "Medo")); + mSDLThread.start(); + gameconnStartDone.set(true); + } + + public void gameConnectionDisconnected(int reason) { + Log.e("startApp", "disconnected: "+reason); + gameconnStartDone.set(true); + } + }; + if(netgame) { + Netplay netplay = Netplay.getAppInstance(mSingleton.getApplicationContext()); + GameConnection.forNetgame(config, netplay, listener); + } else { + GameConnection.forLocalGame(config, listener); + } } else { SDLActivity.nativeResume(); } @@ -426,61 +456,32 @@ class SDLMain implements Runnable { private final int surfaceWidth, surfaceHeight; - private final GameConfig config; - private GameconnPtr conn; + private final int port; + private final String playerName; HandlerThread thread = new HandlerThread("IPC thread"); - private final IntCallback dccb = new IntCallback() { - public void callback(Pointer context, int arg1) { - Log.d("SDLMain", "Disconnected: "+arg1); - Flib.INSTANCE.flib_gameconn_destroy(conn); - conn = null; - thread.quit(); - } - }; - - public SDLMain(int width, int height, GameConfig _config) { - config = _config; + public SDLMain(int width, int height, int port, String playerName) { surfaceWidth = width; surfaceHeight = height; + this.port = port; + this.playerName = playerName; } public void run() { //Set up the IPC socket server to communicate with the engine - - final GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create("Xeli", GameSetupPtr.createJavaOwned(config), false); - if(conn == null) { - throw new AssertionError(); - } - Flib.INSTANCE.flib_gameconn_onDisconnect(conn, dccb, null); - int port = Flib.INSTANCE.flib_gameconn_getport(conn); - - String path = Utils.getDataPath(SDLActivity.mSingleton);//This represents the data directory + String path = FileUtils.getDataPath(SDLActivity.mSingleton);//This represents the data directory path = path.substring(0, path.length()-1);//remove the trailing '/' - thread.start(); // TODO ensure it gets stopped - new TickHandler(thread.getLooper(), 500, new Runnable() { - public void run() { - Log.d("SDLMain", "pre-tick"); - Flib.INSTANCE.flib_gameconn_tick(conn); - Log.d("SDLMain", "post-tick"); - } - }).start(); - Log.d("SDLMain", "Starting engine"); // Runs SDL_main() with added parameters - SDLActivity.nativeInit(new String[] { String.valueOf(port), - String.valueOf(surfaceWidth), String.valueOf(surfaceHeight), - "0", "en.txt", "xeli", "1", "1", "1", path, "" }); + try { + SDLActivity.synchronizedNativeInit(new String[] { String.valueOf(port), + String.valueOf(surfaceWidth), String.valueOf(surfaceHeight), + "0", "en.txt", Base64.encodeToString(playerName.getBytes("UTF-8"), 0), "1", "1", "1", path, "" }); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); // never happens + } Log.d("SDLMain", "Engine stopped"); - try { - thread.join(); - } catch (InterruptedException e) { - throw new AssertionError(); - } - Log.v("SDLMain", "thread joined"); - Flib.INSTANCE.flib_gameconn_destroy(conn); - Log.v("SDLMain", "SDL thread terminated"); } } @@ -495,12 +496,13 @@ View.OnKeyListener, View.OnTouchListener, SensorEventListener { private GameConfig config; - + private boolean netgame; + // Sensors private static SensorManager mSensorManager; // Startup - public SDLSurface(Context context, GameConfig _config) { + public SDLSurface(Context context, GameConfig _config, boolean netgame) { super(context); getHolder().addCallback(this); @@ -512,6 +514,7 @@ mSensorManager = (SensorManager)context.getSystemService("sensor"); config = _config; + this.netgame = netgame; } // Called when we have a valid drawing surface @@ -581,7 +584,7 @@ SDLActivity.onNativeResize(width, height, sdlFormat); Log.v("SDL", "Window size:" + width + "x"+height); - SDLActivity.startApp(width, height, config); + SDLActivity.startApp(width, height, config, netgame); } // unused diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SchemeCreatorActivity.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SchemeCreatorActivity.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,7 @@ +package org.hedgewars.hedgeroid; + +import android.app.Activity; + +public class SchemeCreatorActivity extends Activity { + +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SchemeListActivity.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SchemeListActivity.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,8 @@ +package org.hedgewars.hedgeroid; + +import android.app.Activity; + +// TODO +public class SchemeListActivity extends Activity { + +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SettingsFragment.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SettingsFragment.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,201 @@ +package org.hedgewars.hedgeroid; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils; +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; +import org.hedgewars.hedgeroid.Datastructures.Scheme; +import org.hedgewars.hedgeroid.Datastructures.Schemes; +import org.hedgewars.hedgeroid.Datastructures.Weaponset; +import org.hedgewars.hedgeroid.Datastructures.Weaponsets; +import org.hedgewars.hedgeroid.util.FileUtils; + +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.Spinner; +import android.widget.Toast; + +public class SettingsFragment extends Fragment implements RoomStateManager.Observer { + private Spinner styleSpinner, schemeSpinner, weaponsetSpinner, themeSpinner; + private ImageView themeIcon; + + private List<String> styles; + private List<Scheme> schemes; + private List<Weaponset> weaponsets; + private List<String> themes; + + private RoomStateManager stateManager; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_settings, container, false); + themeIcon = (ImageView)v.findViewById(R.id.imgTheme); + + try { + styles = FrontendDataUtils.getGameStyles(getActivity()); + schemes = Schemes.loadAllSchemes(getActivity()); + weaponsets = Weaponsets.loadAllWeaponsets(getActivity()); + themes = FrontendDataUtils.getThemes(getActivity()); + } catch (IOException e) { + Toast.makeText(getActivity().getApplicationContext(), R.string.error_missing_sdcard_or_files, Toast.LENGTH_LONG).show(); + getActivity().finish(); + } + + Collections.sort(styles, String.CASE_INSENSITIVE_ORDER); + Collections.sort(schemes, Scheme.NAME_ORDER); + Collections.sort(weaponsets, Weaponset.NAME_ORDER); + Collections.sort(themes, String.CASE_INSENSITIVE_ORDER); + + styleSpinner = prepareSpinner(v, R.id.spinGameplay, styles, styleSelectedListener); + schemeSpinner = prepareSpinner(v, R.id.spinGamescheme, Schemes.toNameList(schemes), schemeSelectedListener); + weaponsetSpinner = prepareSpinner(v, R.id.spinweapons, Weaponsets.toNameList(weaponsets), weaponsetSelectedListener); + themeSpinner = prepareSpinner(v, R.id.spinTheme, themes, themeSelectedListener); + + stateManager.registerObserver(this); + + if(stateManager.getGameStyle() != null) { + styleSpinner.setSelection(styles.indexOf(stateManager.getGameStyle()), false); + } + if(stateManager.getScheme() != null) { + schemeSpinner.setSelection(getSchemePosition(schemes, stateManager.getScheme().name), false); + } + if(stateManager.getWeaponset() != null) { + weaponsetSpinner.setSelection(getWeaponsetPosition(weaponsets, stateManager.getWeaponset().name), false); + } + if(stateManager.getMapRecipe() != null) { + themeSpinner.setSelection(themes.indexOf(stateManager.getMapRecipe().theme), false); + } + + setChiefState(stateManager.getChiefStatus()); + + return v; + } + + private static Spinner prepareSpinner(View v, int id, List<String> items, OnItemSelectedListener itemSelectedListener) { + Spinner spinner = (Spinner)v.findViewById(id); + ArrayAdapter<String> adapter = new ArrayAdapter<String>(v.getContext(), R.layout.listview_item, items); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + spinner.setOnItemSelectedListener(itemSelectedListener); + return spinner; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + try { + stateManager = ((RoomStateManager.Provider)getActivity()).getRoomStateManager(); + } catch(ClassCastException e) { + throw new RuntimeException("Hosting activity must implement RoomStateManager.Provider.", e); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + stateManager.unregisterObserver(this); + } + + private static int getSchemePosition(List<Scheme> schemes, String scheme) { + for(int i=0; i<schemes.size(); i++) { + if(schemes.get(i).name.equals(scheme)) { + return i; + } + } + return -1; + } + + private static int getWeaponsetPosition(List<Weaponset> weaponsets, String weaponset) { + for(int i=0; i<weaponsets.size(); i++) { + if(weaponsets.get(i).name.equals(weaponset)) { + return i; + } + } + return -1; + } + + private void setChiefState(boolean chiefState) { + styleSpinner.setEnabled(chiefState); + schemeSpinner.setEnabled(chiefState); + weaponsetSpinner.setEnabled(chiefState); + themeSpinner.setEnabled(chiefState); + + if(chiefState) { + stateManager.changeGameStyle(styles.get(styleSpinner.getSelectedItemPosition())); + stateManager.changeScheme(schemes.get(schemeSpinner.getSelectedItemPosition())); + stateManager.changeWeaponset(weaponsets.get(weaponsetSpinner.getSelectedItemPosition())); + stateManager.changeMapTheme(themes.get(themeSpinner.getSelectedItemPosition())); + } + } + + private final OnItemSelectedListener styleSelectedListener = new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) { + stateManager.changeGameStyle(styles.get(position)); + } + public void onNothingSelected(AdapterView<?> arg0) {} + }; + + private final OnItemSelectedListener schemeSelectedListener = new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) { + stateManager.changeScheme(schemes.get(position)); + } + public void onNothingSelected(AdapterView<?> arg0) {} + }; + + private final OnItemSelectedListener weaponsetSelectedListener = new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) { + stateManager.changeWeaponset(weaponsets.get(position)); + } + public void onNothingSelected(AdapterView<?> arg0) {} + }; + + private final OnItemSelectedListener themeSelectedListener = new OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) { + stateManager.changeMapTheme(themes.get(position)); + String theme = themes.get(position); + try { + File iconFile = new File(FileUtils.getDataPathFile(getActivity()), "Themes/" + theme + "/icon@2X.png"); + Drawable themeIconDrawable = Drawable.createFromPath(iconFile.getAbsolutePath()); + themeIcon.setImageDrawable(themeIconDrawable); + } catch (FileNotFoundException e) { + Log.e("SettingsFragment", "Unable to find preview for theme "+theme, e); + } + }; + public void onNothingSelected(AdapterView<?> arg0) {}; + }; + + public void onChiefStatusChanged(boolean isChief) { + setChiefState(isChief); + } + + public void onGameStyleChanged(String gameStyle) { + styleSpinner.setSelection(styles.indexOf(gameStyle)); + } + + public void onMapChanged(MapRecipe recipe) { + themeSpinner.setSelection(themes.indexOf(recipe.theme)); + } + + public void onSchemeChanged(Scheme scheme) { + schemeSpinner.setSelection(getSchemePosition(schemes, scheme.name)); + } + + public void onWeaponsetChanged(Weaponset weaponset) { + weaponsetSpinner.setSelection(getWeaponsetPosition(weaponsets, weaponset.name)); + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java Sat Aug 18 00:47:51 2012 +0200 @@ -36,12 +36,12 @@ import org.hedgewars.hedgeroid.Datastructures.TeamInGame; import org.hedgewars.hedgeroid.Datastructures.Weaponset; import org.hedgewars.hedgeroid.Datastructures.Weaponsets; +import org.hedgewars.hedgeroid.util.FileUtils; import android.app.Activity; import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView; @@ -56,8 +56,14 @@ public static final int ACTIVITY_TEAM_SELECTOR = 0; private ImageButton start, back, team; - private Spinner maps, gameplay, gamescheme, weapons, themes; + private Spinner mapSpinner, styleSpinner, schemeSpinner, weaponsetSpinner, themeSpinner; private ImageView themeIcon, mapPreview, teamCount; + + private List<MapFile> mapFiles; + private List<String> styles; + private List<Scheme> schemes; + private List<Weaponset> weaponsets; + private List<String> themes; private List<TeamInGame> teams = new ArrayList<TeamInGame>(); @@ -70,12 +76,6 @@ team = (ImageButton) findViewById(R.id.btnTeams); start = (ImageButton) findViewById(R.id.btnStart); - maps = (Spinner) findViewById(R.id.spinMaps); - gameplay = (Spinner) findViewById(R.id.spinGameplay); - gamescheme = (Spinner) findViewById(R.id.spinGamescheme); - weapons = (Spinner) findViewById(R.id.spinweapons); - themes = (Spinner) findViewById(R.id.spinTheme); - themeIcon = (ImageView) findViewById(R.id.imgTheme); mapPreview = (ImageView) findViewById(R.id.mapPreview); teamCount = (ImageView) findViewById(R.id.imgTeamsCount); @@ -84,90 +84,55 @@ back.setOnClickListener(backClicker); team.setOnClickListener(teamClicker); - List<MapFile> mapFiles; try { mapFiles = FrontendDataUtils.getMaps(this); - } catch (FileNotFoundException e) { - Log.e("StartGameActivity", e.getMessage(), e); - mapFiles = Collections.emptyList(); - } - ArrayAdapter<?> adapter = new ArrayAdapter<MapFile>(this, R.layout.listview_item, mapFiles); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - maps.setAdapter(adapter); - maps.setOnItemSelectedListener(mapsClicker); - //set to first nonmission - for(int i = 0; i < adapter.getCount(); i++){ - if(!((MapFile)adapter.getItem(i)).isMission){ - maps.setSelection(i, false); - break; - } + styles = FrontendDataUtils.getGameStyles(this); + schemes = Schemes.loadAllSchemes(this); + weaponsets = Weaponsets.loadAllWeaponsets(this); + themes = FrontendDataUtils.getThemes(this); + } catch (IOException e) { + Toast.makeText(getApplicationContext(), R.string.error_missing_sdcard_or_files, Toast.LENGTH_LONG).show(); + finish(); } - - List<String> gameStyles; - try { - gameStyles = FrontendDataUtils.getGameStyles(this); - } catch (FileNotFoundException e) { - Log.e("StartGameActivity", e.getMessage(), e); - gameStyles = Collections.emptyList(); - } - adapter = new ArrayAdapter<String>(this, R.layout.listview_item, gameStyles); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - gameplay.setAdapter(adapter); - //set to first nonmap - for(int i = 0; i < adapter.getCount(); i++){ - if(((String)adapter.getItem(i)).equals("None")){ - gameplay.setSelection(i, false); + + Collections.sort(mapFiles, MapFile.MISSIONS_FIRST_NAME_ORDER); + Collections.sort(styles, String.CASE_INSENSITIVE_ORDER); + Collections.sort(schemes, Scheme.NAME_ORDER); + Collections.sort(weaponsets, Weaponset.NAME_ORDER); + Collections.sort(themes, String.CASE_INSENSITIVE_ORDER); + + List<String> mapNames = MapFile.toDisplayNameList(mapFiles, getResources()); + List<String> schemeNames = Schemes.toNameList(schemes); + List<String> weaponsetNames = Weaponsets.toNameList(weaponsets); + View rootView = findViewById(android.R.id.content); + mapSpinner = prepareSpinner(rootView, R.id.spinMaps, mapNames, mapsClicker); + styleSpinner = prepareSpinner(rootView, R.id.spinGameplay, styles, null); + schemeSpinner = prepareSpinner(rootView, R.id.spinGamescheme, schemeNames, null); + weaponsetSpinner = prepareSpinner(rootView, R.id.spinweapons, weaponsetNames, null); + themeSpinner = prepareSpinner(rootView, R.id.spinTheme, themes, themesClicker); + + // set map to first nonmission + for(int i = 0; i < mapFiles.size(); i++){ + if(!mapFiles.get(i).isMission){ + mapSpinner.setSelection(i, false); break; } } - - List<Scheme> schemes; - try { - schemes = new ArrayList<Scheme>(Schemes.loadAllSchemes(this).values()); - } catch (IOException e) { - Log.e("StartGameActivity", e.getMessage(), e); - schemes = Collections.emptyList(); - } - adapter = new ArrayAdapter<Scheme>(this, R.layout.listview_item, schemes); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - gamescheme.setAdapter(adapter); - for(int i = 0; i < adapter.getCount(); i++){ - if(((Scheme)adapter.getItem(i)).name.equals("Default")){ - gamescheme.setSelection(i, false); - break; - } - } - - List<Weaponset> weaponsets; - try { - weaponsets = Weaponsets.loadAllWeaponsets(this); - } catch(IOException e) { - Log.e("StartGameActivity", e.getMessage(), e); - weaponsets = Collections.emptyList(); - } - adapter = new ArrayAdapter<Weaponset>(this, R.layout.listview_item, weaponsets); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - weapons.setAdapter(adapter); - for(int i = 0; i < adapter.getCount(); i++){ - if(((Weaponset)adapter.getItem(i)).name.equals("Crazy")){ - weapons.setSelection(i, false); - break; - } - } - - List<String> themeList; - try { - themeList = FrontendDataUtils.getThemes(this); - } catch(FileNotFoundException e) { - Log.e("StartGameActivity", e.getMessage(), e); - themeList = Collections.emptyList(); - } - adapter = new ArrayAdapter<String>(this, R.layout.listview_item, themeList); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - themes.setAdapter(adapter); - themes.setOnItemSelectedListener(themesClicker); + styleSpinner.setSelection(styles.indexOf(GameConfig.DEFAULT_STYLE), false); + schemeSpinner.setSelection(schemeNames.indexOf(GameConfig.DEFAULT_SCHEME), false); + weaponsetSpinner.setSelection(weaponsetNames.indexOf(GameConfig.DEFAULT_WEAPONSET), false); + themeSpinner.setSelection(themes.indexOf(GameConfig.DEFAULT_THEME), false); } + private static Spinner prepareSpinner(View v, int id, List<String> items, OnItemSelectedListener itemSelectedListener) { + Spinner spinner = (Spinner)v.findViewById(id); + ArrayAdapter<String> adapter = new ArrayAdapter<String>(v.getContext(), R.layout.listview_item, items); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + spinner.setOnItemSelectedListener(itemSelectedListener); + return spinner; + } + private void startTeamsActivity(){ Intent i = new Intent(StartGameActivity.this, TeamSelectionActivity.class); TeamSelectionActivity.activityParams = new ArrayList<TeamInGame>(teams); @@ -190,8 +155,8 @@ private OnItemSelectedListener themesClicker = new OnItemSelectedListener(){ public void onItemSelected(AdapterView<?> arg0, View view, int position, long rowId) { - String themeName = (String) arg0.getAdapter().getItem(position); - Drawable themeIconDrawable = Drawable.createFromPath(Utils.getDataPath(StartGameActivity.this) + "Themes/" + themeName + "/icon@2X.png"); + String themeName = themes.get(position); + Drawable themeIconDrawable = Drawable.createFromPath(FileUtils.getDataPath(StartGameActivity.this) + "Themes/" + themeName + "/icon@2X.png"); themeIcon.setImageDrawable(themeIconDrawable); } @@ -203,7 +168,7 @@ private OnItemSelectedListener mapsClicker = new OnItemSelectedListener(){ public void onItemSelected(AdapterView<?> arg0, View view, int position,long rowId) { - MapFile map = (MapFile)arg0.getAdapter().getItem(position); + MapFile map = mapFiles.get(position); try { File previewFile = map.getPreviewFile(getApplicationContext()); mapPreview.setImageDrawable(Drawable.createFromPath(previewFile.getAbsolutePath())); @@ -223,13 +188,14 @@ Toast.makeText(getApplicationContext(), R.string.not_enough_teams, Toast.LENGTH_LONG).show(); startTeamsActivity(); } else { - String style = (String)gameplay.getSelectedItem(); - Scheme scheme = (Scheme)gamescheme.getSelectedItem(); - String mapName = ((MapFile)maps.getSelectedItem()).name; - String theme = (String)themes.getSelectedItem(); + String style = styles.get(styleSpinner.getSelectedItemPosition()); + Scheme scheme = schemes.get(schemeSpinner.getSelectedItemPosition()); + String mapName = mapFiles.get(mapSpinner.getSelectedItemPosition()).name; + String theme = themes.get(themeSpinner.getSelectedItemPosition()); MapRecipe map = MapRecipe.makeMap(mapName, UUID.randomUUID().toString(), theme); - Weaponset weaponset = (Weaponset)weapons.getSelectedItem(); + Weaponset weaponset = weaponsets.get(weaponsetSpinner.getSelectedItemPosition()); SDLActivity.startConfig = new GameConfig(style, scheme, map, teams, weaponset); + SDLActivity.startNetgame = false; Intent i = new Intent(StartGameActivity.this, SDLActivity.class); startActivity(i); } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamAddDialog.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamAddDialog.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,92 @@ +package org.hedgewars.hedgeroid; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils; +import org.hedgewars.hedgeroid.Datastructures.Team; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; + +public class TeamAddDialog extends DialogFragment { + private static final String STATE_TEAMS_ALREADY_IN_GAME = "teamAlreadyInGame"; + private ArrayList<String> teamsAlreadyInGame; + private List<Team> availableTeams; + private Listener listener; + + public static interface Listener { + void onTeamAddDialogSubmitted(Team newTeam); + } + + public TeamAddDialog() { + // Only for reflection-based instantiation by the framework + } + + TeamAddDialog(Collection<String> teamsAlreadyInGame) { + this.teamsAlreadyInGame = new ArrayList<String>(teamsAlreadyInGame); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + listener = (Listener) activity; + } catch(ClassCastException e) { + throw new ClassCastException("Activity " + activity + " must implement TeamAddDialog.Listener to use TeamAddDialog."); + } + } + + @Override + public void onDetach() { + super.onDetach(); + listener = null; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if(savedInstanceState != null) { + teamsAlreadyInGame = savedInstanceState.getStringArrayList(STATE_TEAMS_ALREADY_IN_GAME); + } + availableTeams = new ArrayList<Team>(); + List<Team> teams = FrontendDataUtils.getTeams(getActivity()); + for(Team team : teams) { + if(!teamsAlreadyInGame.contains(team.name)) { + availableTeams.add(team); + } + } + Collections.sort(availableTeams, Team.NAME_ORDER); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.dialog_addteam_title); + builder.setIcon(R.drawable.human); + String[] teamNames = new String[availableTeams.size()]; + for(int i=0; i<availableTeams.size(); i++) { + teamNames[i] = availableTeams.get(i).name; + } + builder.setItems(teamNames, new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + listener.onTeamAddDialogSubmitted(availableTeams.get(which)); + } + }); + return builder.create(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putStringArrayList(STATE_TEAMS_ALREADY_IN_GAME, teamsAlreadyInGame); + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java Sat Aug 18 00:47:51 2012 +0200 @@ -31,6 +31,7 @@ import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils; import org.hedgewars.hedgeroid.Datastructures.Hog; import org.hedgewars.hedgeroid.Datastructures.Team; +import org.hedgewars.hedgeroid.util.FileUtils; import android.app.Activity; import android.graphics.Bitmap; @@ -253,41 +254,13 @@ super.onBackPressed(); } - private int getDifficultyLevelFromText(String text) { - if (text.equals(getString(R.string.human))) { - return 0; - } else if (text.equals(getString(R.string.bot5))) { - return 1; - } else if (text.equals(getString(R.string.bot4))) { - return 2; - } else if (text.equals(getString(R.string.bot3))) { - return 3; - } else if (text.equals(getString(R.string.bot2))) { - return 4; - } else { - return 5; - } - } - - private String getTxtFromDifficulty(int level) { - switch(level) { - case 0: return getString(R.string.human); - case 1: return getString(R.string.bot5); - case 2: return getString(R.string.bot4); - case 3: return getString(R.string.bot3); - case 4: return getString(R.string.bot2); - default: return getString(R.string.bot1); - } - } - private void saveTeam() { String teamName = name.getText().toString(); String teamFlag = (String)((Map<String, Object>) flag.getSelectedItem()).get("txt"); String teamFort = fort.getSelectedItem().toString(); String teamGrave = (String)((Map<String, Object>) grave.getSelectedItem()).get("txt"); String teamVoice = voice.getSelectedItem().toString(); - String levelString = (String)((Map<String, Object>) difficulty.getSelectedItem()).get("txt"); - int levelInt = getDifficultyLevelFromText(levelString); + int levelInt = (Integer)((Map<String, Object>) difficulty.getSelectedItem()).get("level"); List<Hog> hogs = new ArrayList<Hog>(); for (int i = 0; i < hogName.size(); i++) { @@ -307,7 +280,6 @@ } try { team.save(newFile); - Toast.makeText(TeamCreatorActivity.this, R.string.saved, Toast.LENGTH_SHORT).show(); // If the team was renamed, delete the old file. if(oldFile != null && oldFile.isFile() && !oldFile.equals(newFile)) { oldFile.delete(); @@ -322,7 +294,7 @@ public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long arg3) { String fortName = (String) arg0.getAdapter().getItem(position); - Drawable fortIconDrawable = Drawable.createFromPath(Utils + Drawable fortIconDrawable = Drawable.createFromPath(FileUtils .getDataPath(TeamCreatorActivity.this) + "Forts/" + fortName + "L.png"); @@ -343,7 +315,7 @@ public void onClick(View v) { try { File dir = new File(String.format("%sSounds/voices/%s", - Utils.getDataPath(TeamCreatorActivity.this), + FileUtils.getDataPath(TeamCreatorActivity.this), voice.getSelectedItem())); String file = ""; File[] dirs = dir.listFiles(); @@ -378,12 +350,12 @@ name.setText(t.name); voice.setSelection(findPosition((ArrayAdapter<String>) voice.getAdapter(), t.voice)); fort.setSelection(findPosition((ArrayAdapter<String>) fort.getAdapter(), t.fort)); - difficulty.setSelection(findPosition(typesData, getTxtFromDifficulty(t.hogs.get(0).level))); // TODO store actual difficulty int in typesData - grave.setSelection(findPosition(gravesData, t.grave)); - flag.setSelection(findPosition(flagsData, t.flag)); + difficulty.setSelection(findPosition(typesData, "level", Integer.valueOf(t.hogs.get(0).level))); + grave.setSelection(findPosition(gravesData, "txt", t.grave)); + flag.setSelection(findPosition(flagsData, "txt", t.flag)); for (int i = 0; i < Team.HEDGEHOGS_PER_TEAM; i++) { - hogHat.get(i).setSelection(findPosition(hatsData, t.hogs.get(i).hat)); + hogHat.get(i).setSelection(findPosition(hatsData, "txt", t.hogs.get(i).hat)); hogName.get(i).setText(t.hogs.get(i).name); } } catch(NoSuchElementException e) { @@ -392,18 +364,18 @@ } } - int findPosition(ArrayAdapter<String> adapter, String key) throws NoSuchElementException { - int position = adapter.getPosition(key); + int findPosition(ArrayAdapter<String> adapter, String value) throws NoSuchElementException { + int position = adapter.getPosition(value); if(position<0) { throw new NoSuchElementException(); } return position; } - int findPosition(List<? extends Map<String, ?>> data, String txtValue) throws NoSuchElementException { + int findPosition(List<? extends Map<String, ?>> data, String key, Object value) throws NoSuchElementException { int position = 0; for (Map<String, ?> map : data) { - if (map.get("txt").equals(txtValue)) { + if (map.get(key).equals(value)) { return position; } position++; diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamSelectionActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamSelectionActivity.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamSelectionActivity.java Sat Aug 18 00:47:51 2012 +0200 @@ -125,7 +125,7 @@ setTeamColor(view, (Integer)data); return true; case R.id.teamCount: - setTeamHogCount((ImageView)view, (Integer)data); + ((ImageView)view).getDrawable().setLevel((Integer)data); return true; default: return false; @@ -179,42 +179,6 @@ iv.setBackgroundColor(0xFF000000 + TeamIngameAttributes.TEAM_COLORS[colorIndex]); } - private void setTeamHogCount(ImageView iv, int count){ - - switch(count){ - case 0: - iv.setImageResource(R.drawable.teamcount0); - break; - case 1: - iv.setImageResource(R.drawable.teamcount1); - break; - case 2: - iv.setImageResource(R.drawable.teamcount2); - break; - case 3: - iv.setImageResource(R.drawable.teamcount3); - break; - case 4: - iv.setImageResource(R.drawable.teamcount4); - break; - case 5: - iv.setImageResource(R.drawable.teamcount5); - break; - case 6: - iv.setImageResource(R.drawable.teamcount6); - break; - case 7: - iv.setImageResource(R.drawable.teamcount7); - break; - case 8: - iv.setImageResource(R.drawable.teamcount8); - break; - case 9: - iv.setImageResource(R.drawable.teamcount9); - break; - } - } - public void onBackPressed(){ returnTeams(); super.onBackPressed(); diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamlistAdapter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamlistAdapter.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,112 @@ +package org.hedgewars.hedgeroid; + +import java.util.Comparator; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.TeamInGame; +import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes; +import org.hedgewars.hedgeroid.util.ObservableTreeMapAdapter; + +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.TextView; + +public class TeamlistAdapter extends ObservableTreeMapAdapter<String, TeamInGame> { + private boolean colorHogcountEnabled = false; + private Listener listener; + + @Override + protected Comparator<TeamInGame> getEntryOrder() { + return TeamInGame.NAME_ORDER; + } + + public void setColorHogcountEnabled(boolean colorHogcountEnabled) { + this.colorHogcountEnabled = colorHogcountEnabled; + notifyDataSetChanged(); + } + + public void setListener(Listener listener) { + this.listener = listener; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View v = convertView; + if (v == null) { + LayoutInflater vi = LayoutInflater.from(parent.getContext()); + v = vi.inflate(R.layout.listview_team, null); + } + + TeamInGame team = getItem(position); + TextView teamNameView = (TextView) v.findViewById(android.R.id.text1); + ImageButton colorButton = (ImageButton) v.findViewById(R.id.colorButton); + ImageButton hogCountButton = (ImageButton) v.findViewById(R.id.hogCountButton); + + teamNameView.setText(team.team.name); + int teamImage; + if(team.ingameAttribs.remoteDriven) { + teamImage = R.drawable.team_net_by_level; + } else { + teamImage = R.drawable.team_local_by_level; + } + + Drawable d = parent.getContext().getResources().getDrawable(teamImage).mutate(); + d.setLevel(team.team.hogs.get(0).level); + teamNameView.setCompoundDrawablesWithIntrinsicBounds(d, null, null, null); + hogCountButton.getDrawable().setLevel(team.ingameAttribs.hogCount); + colorButton.setImageDrawable(new ColorDrawable(TeamIngameAttributes.TEAM_COLORS[team.ingameAttribs.colorIndex])); + + colorButton.setEnabled(colorHogcountEnabled); + hogCountButton.setEnabled(colorHogcountEnabled); + + colorButton.setOnClickListener(new ButtonClickListener(team, Type.COLOR_BUTTON)); + hogCountButton.setOnClickListener(new ButtonClickListener(team, Type.HOGCOUNT_BUTTON)); + + if(team.ingameAttribs.remoteDriven) { + teamNameView.setClickable(false); + } else { + teamNameView.setOnClickListener(new ButtonClickListener(team, Type.TEAM_VIEW)); + } + + return v; + } + + private static enum Type {COLOR_BUTTON, HOGCOUNT_BUTTON, TEAM_VIEW} + private final class ButtonClickListener implements OnClickListener { + private final TeamInGame team; + private final Type type; + + public ButtonClickListener(TeamInGame team, Type type) { + this.team = team; + this.type = type; + } + + public void onClick(View v) { + if(listener != null) { + switch(type) { + case COLOR_BUTTON: + listener.onColorClicked(team); + break; + case HOGCOUNT_BUTTON: + listener.onHogcountClicked(team); + break; + case TEAM_VIEW: + listener.onTeamClicked(team); + break; + default: + throw new IllegalStateException(); + } + } + } + } + + public interface Listener { + void onTeamClicked(TeamInGame team); + void onColorClicked(TeamInGame team); + void onHogcountClicked(TeamInGame team); + } +} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamlistFragment.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamlistFragment.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,122 @@ +package org.hedgewars.hedgeroid; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; +import org.hedgewars.hedgeroid.Datastructures.Scheme; +import org.hedgewars.hedgeroid.Datastructures.Team; +import org.hedgewars.hedgeroid.Datastructures.TeamInGame; +import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes; +import org.hedgewars.hedgeroid.Datastructures.Weaponset; +import org.hedgewars.hedgeroid.netplay.Netplay; + +import android.database.DataSetObserver; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.Button; + +/** + * TODO use an interface for querying and manipulating the team list, to allow re-using this fragment + * in local play + */ +public class TeamlistFragment extends ListFragment implements TeamlistAdapter.Listener, RoomStateManager.Observer { + private Netplay netplay; + private TeamlistAdapter adapter; + private Button addTeamButton; + private DataSetObserver teamlistObserver; + private RoomStateManager stateManager; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + try { + stateManager = ((RoomStateManager.Provider)getActivity()).getRoomStateManager(); + } catch(ClassCastException e) { + throw new RuntimeException("Hosting activity must implement RoomStateManager.Provider.", e); + } + netplay = Netplay.getAppInstance(getActivity().getApplicationContext()); + adapter = new TeamlistAdapter(); + adapter.setSource(netplay.roomTeamlist); + adapter.setColorHogcountEnabled(stateManager.getChiefStatus()); + adapter.setListener(this); + setListAdapter(adapter); + stateManager.registerObserver(this); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_teamlist, container, false); + addTeamButton = (Button)v.findViewById(R.id.addTeamButton); + addTeamButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + new TeamAddDialog(getCurrentTeamNames()).show(getFragmentManager(), "team_add_dialog"); + } + }); + + teamlistObserver = new DataSetObserver() { + @Override + public void onChanged() { + addTeamButton.setEnabled(netplay.roomTeamlist.getMap().size() < Team.maxNumberOfTeams); + } + }; + netplay.roomTeamlist.registerObserver(teamlistObserver); + teamlistObserver.onChanged(); + + return v; + } + + @Override + public void onDestroy() { + super.onDestroy(); + adapter.invalidate(); + adapter.setListener(null); + netplay.roomTeamlist.unregisterObserver(teamlistObserver); + stateManager.unregisterObserver(this); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + } + + private Collection<String> getCurrentTeamNames() { + List<String> names = new ArrayList<String>(); + for(TeamInGame team : netplay.roomTeamlist.getMap().values()) { + names.add(team.team.name); + } + return names; + } + + public void onColorClicked(TeamInGame team) { + netplay.sendTeamColorIndex(team.team.name, (team.ingameAttribs.colorIndex+1)%TeamIngameAttributes.TEAM_COLORS.length); + } + + public void onHogcountClicked(TeamInGame team) { + int newHogCount = team.ingameAttribs.hogCount+1; + if(newHogCount>Team.HEDGEHOGS_PER_TEAM) { + newHogCount = 1; + } + netplay.sendTeamHogCount(team.team.name, newHogCount); + } + + public void onTeamClicked(TeamInGame team) { + netplay.sendRemoveTeam(team.team.name); + } + + public void onChiefStatusChanged(boolean isChief) { + adapter.setColorHogcountEnabled(isChief); + } + + public void onGameStyleChanged(String gameStyle) { } + public void onMapChanged(MapRecipe recipe) { } + public void onSchemeChanged(Scheme scheme) { } + public void onWeaponsetChanged(Weaponset weaponset) { } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,273 +0,0 @@ -/* - * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game - * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - - -package org.hedgewars.hedgeroid; - -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.os.Build; -import android.os.Environment; -import android.util.Log; - -public class Utils { - private static final String ROOT_DIR = "Data"; - private static final String TAG = "org.hedgewars.hedgeroid"; - - /** - * @return true if the data path is currently available. However, it can vanish at any time so - * normally you should just try to use it and rely on the exceptions. - */ - public static boolean isDataPathAvailable() { - return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); - } - - /** - * get the path to which we should download all the data files - * @param c context - * @return The directory - * @throws FileNotFoundException if external storage is not available at the moment - */ - public static File getCachePath(Context c) throws FileNotFoundException { - File cachePath = null; - if(Build.VERSION.SDK_INT < 8){//8 == Build.VERSION_CODES.FROYO - cachePath = PreFroyoSDCardDir.getDownloadPath(c); - } else { - cachePath = FroyoSDCardDir.getDownloadPath(c); - } - if(cachePath==null) { - throw new FileNotFoundException("External storage is currently unavailable"); - } else { - return cachePath; - } - } - - public static File getDataPathFile(Context c) throws FileNotFoundException { - return new File(getCachePath(c), ROOT_DIR); - } - - // TODO Several callers are unaware that this may fail, so it throws an RTE now. - // Should be handled better though. - @Deprecated - public static String getDataPath(Context c) { - try { - return getDataPathFile(c).getAbsolutePath()+"/"; - } catch(FileNotFoundException e) { - throw new RuntimeException(e); - } - } - - @TargetApi(8) - private static class FroyoSDCardDir{ - public static File getDownloadPath(Context c){ - return c.getExternalCacheDir(); - } - } - - private static class PreFroyoSDCardDir{ - public static File getDownloadPath(Context c){ - if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ - File extStorageDir = Environment.getExternalStorageDirectory(); - if(extStorageDir != null) { - return new File(extStorageDir, "Hedgewars"); - } - } - return null; - } - } - - /** - * Return a File array with all the files from dirName - * @param c - * @param dirName - * @return - * @throws FileNotFoundException If the sdcard is not available or the subdirectory "dirName" does not exist - */ - public static File[] getFilesFromRelativeDir(Context c, String dirName) throws FileNotFoundException { - File f = new File(getDataPathFile(c), dirName); - - if(f.isDirectory()) { - return f.listFiles(); - } else { - throw new FileNotFoundException("Directory "+dirName+" does not exist."); - } - } - - /** - * Checks if this directory has a file with suffix suffix - * @param f - directory - * @return - */ - public static boolean hasFileWithSuffix(File f, String suffix){ - if(f.isDirectory()){ - for(String s : f.list()){ - if(s.endsWith(suffix)) return true; - } - return false; - }else{ - return false; - } - } - - /** - * Gives back all dirs which contain a file with suffix fileSuffix - * @param c - * @param path - * @param fileSuffix - * @return - * @throws FileNotFoundException If the sdcard is not available or the subdirectory "path" does not exist - */ - public static List<String> getDirsWithFileSuffix(Context c, String path, String fileSuffix) throws FileNotFoundException{ - File[] files = getFilesFromRelativeDir(c,path); - ArrayList<String> ret = new ArrayList<String>(); - - for(File f : files){ - if(hasFileWithSuffix(f, fileSuffix)) ret.add(f.getName()); - } - return ret; - } - - /** - * Get all files from directory dir which have the given suffix - * @throws FileNotFoundException If the sdcard is not available or the subdirectory "dir" does not exist - */ - public static ArrayList<String> getFileNamesFromDirWithSuffix(Context c, String dir, String suffix, boolean removeSuffix) throws FileNotFoundException{ - File[] files = Utils.getFilesFromRelativeDir(c, dir); - ArrayList<String> ret = new ArrayList<String>(); - for(File file : files){ - String s = file.getName(); - if(s.endsWith(suffix)){ - if(removeSuffix) ret.add(s.substring(0, s.length()-suffix.length())); - else ret.add(s); - } - } - return ret; - } - - /** - * Close a resource (possibly null), ignoring any IOException. - */ - public static void closeQuietly(Closeable c) { - if(c!=null) { - try { - c.close(); - } catch(IOException e) { - Log.w(TAG, e); - } - } - } - - /** - * Write all data from the input stream to the file, creating or overwriting it. - * The input stream will be closed. - * - * @throws IOException - */ - public static void writeStreamToFile(InputStream is, File file) throws IOException { - OutputStream os = null; - byte[] buffer = new byte[8192]; - try { - os = new FileOutputStream(file); - int size; - while((size=is.read(buffer)) != -1) { - os.write(buffer, 0, size); - } - os.close(); // Important to close this non-quietly, in case of exceptions when flushing - } finally { - Utils.closeQuietly(is); - Utils.closeQuietly(os); - } - } - - /** - * Moves resources pointed to by sourceResId (from @res/raw/) to the app's private data directory - * @param c - * @param sourceResId - * @param directory - */ - public static void resRawToFilesDir(Context c, int sourceResId, String directory) throws IOException { - File targetDir = new File(c.getFilesDir(), directory); - targetDir.mkdirs(); - - //Get an array with the resource files ID - Resources resources = c.getResources(); - TypedArray ta = resources.obtainTypedArray(sourceResId); - for(int i = 0; i < ta.length(); i++){ - int resId = ta.getResourceId(i, 0); - String fileName = resources.getResourceEntryName(resId); - File f = new File(targetDir, fileName); - writeStreamToFile(resources.openRawResource(resId), f); - } - } - - /** - * Crashing - at least it's better than ignoring errors. - */ - public static void assertZero(int value, String text) { - if(value != 0) { - throw new RuntimeException("Result is not zero: " + text); - } - } - - public static String readToString(InputStream is) throws IOException { - try { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - byte[] buffer = new byte[8192]; - int size; - while((size=is.read(buffer)) != -1) { - os.write(buffer, 0, size); - } - return new String(os.toByteArray()); - } finally { - closeQuietly(is); - } - } - - private static final char[] badFilenameChars = new char[] { '/', '\\', ':', '*', '?', '\"', '<', '>', '|', '.', '\0' }; - - /** - * Modify the given String so that it can be used as part of a filename - * without causing problems from illegal/special characters. - * - * The result should be similar to the input, but isn't necessarily - * reversible. - */ - public static String replaceBadChars(String name) { - if (name == null || name.trim().length()==0) { - return "_"; - } - name = name.trim(); - for (char badChar : badFilenameChars) { - name = name.replace(badChar, '_'); - } - return name; - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/WeaponsetCreatorActivity.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/WeaponsetCreatorActivity.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,8 @@ +package org.hedgewars.hedgeroid; + +import android.support.v4.app.FragmentActivity; + +// TODO +public class WeaponsetCreatorActivity extends FragmentActivity { + public static final String PARAMETER_EXISTING_WEAPONSETNAME="existingWeaponsetName"; +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/WeaponsetListActivity.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/WeaponsetListActivity.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,107 @@ +package org.hedgewars.hedgeroid; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.hedgewars.hedgeroid.Datastructures.Weaponset; +import org.hedgewars.hedgeroid.Datastructures.Weaponsets; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.view.ContextMenu; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.AdapterView; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.Button; +import android.widget.ListAdapter; +import android.widget.SimpleAdapter; +import android.widget.Toast; + +public class WeaponsetListActivity extends ListActivity implements OnItemClickListener { + private List<Weaponset> userWeaponsets; + private Button addButton; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_weaponsetlist); + addButton = (Button)findViewById(R.id.addButton); + addButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + editWeaponset(null); + } + }); + } + + @Override + public void onResume() { + super.onResume(); + updateList(); + getListView().setOnItemClickListener(this); + registerForContextMenu(getListView()); + } + + private List<Map<String, ?>> weaponsetsToMap(List<Weaponset> weaponsets) { + List<Map<String, ?>> result = new ArrayList<Map<String, ?>>(); + for(Weaponset weaponset : weaponsets) { + result.add(Collections.singletonMap("txt", weaponset.name)); + } + return result; + } + + public void onItemClick(AdapterView<?> adapterView, View v, int position, long arg3) { + editWeaponset(userWeaponsets.get(position).name); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuinfo){ + menu.add(ContextMenu.NONE, 0, ContextMenu.NONE, R.string.edit); + menu.add(ContextMenu.NONE, 1, ContextMenu.NONE, R.string.delete); + } + + @Override + public boolean onContextItemSelected(MenuItem item){ + AdapterView.AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo(); + int position = menuInfo.position; + Weaponset weaponset = userWeaponsets.get(position); + switch(item.getItemId()){ + case 0: + editWeaponset(weaponset.name); + return true; + case 1: + try { + Weaponsets.deleteUserWeaponset(this, weaponset.name); + } catch (IOException e) { + Toast.makeText(this.getApplicationContext(), R.string.error_missing_sdcard_or_files, Toast.LENGTH_SHORT).show(); + } + updateList(); + return true; + } + return false; + } + + private void updateList() { + try { + userWeaponsets = Weaponsets.loadUserWeaponsets(this); + } catch (IOException e) { + Toast.makeText(this, R.string.error_missing_sdcard_or_files, Toast.LENGTH_LONG).show(); + finish(); + } + Collections.sort(userWeaponsets, Weaponset.NAME_ORDER); + ListAdapter adapter = new SimpleAdapter(this, weaponsetsToMap(userWeaponsets), android.R.layout.simple_list_item_1, new String[]{"txt"}, new int[]{android.R.id.text1}); + setListAdapter(adapter); + } + + private void editWeaponset(String weaponsetName) { + Intent i = new Intent(this, WeaponsetCreatorActivity.class); + i.putExtra(WeaponsetCreatorActivity.PARAMETER_EXISTING_WEAPONSETNAME, weaponsetName); + startActivity(i); + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java Sat Aug 18 00:47:51 2012 +0200 @@ -12,7 +12,7 @@ import org.hedgewars.hedgeroid.Datastructures.MetaScheme.Mod; import org.hedgewars.hedgeroid.Datastructures.MetaScheme.Setting; import org.hedgewars.hedgeroid.Datastructures.GameConfig; -import org.hedgewars.hedgeroid.Datastructures.RoomlistRoom; +import org.hedgewars.hedgeroid.Datastructures.Room; import org.hedgewars.hedgeroid.Datastructures.Scheme; import org.hedgewars.hedgeroid.Datastructures.Team; import org.hedgewars.hedgeroid.Datastructures.TeamInGame; @@ -26,7 +26,6 @@ import com.sun.jna.Pointer; import com.sun.jna.PointerType; import com.sun.jna.Structure; -import com.sun.jna.ptr.IntByReference; /** * Here is an introduction to the most important aspects of the JNA code. @@ -51,14 +50,12 @@ * representing the data (e.g. SchemePtr.deref() will give you a Scheme object). * * Remember that you usually have to destroy structs that you receive from the - * library, because they are owned by the native code, not Java. For example, if - * you obtain a {@link MetaschemePtr} metaPtr using flib_metascheme_from_ini, - * you have to call flib_metascheme_release(metaPtr) after you are done using - * it. The recommended pattern for most cases is to call deref() on the pointer - * to get a Java object (that you can keep as long as you like), and then - * immediately destroy the struct if it needs destroying. To find out whether - * and how the struct needs to be destroyed, see the library's documentation of - * the function that you got the struct from. + * library, because they are owned by the native code, not Java. The recommended + * pattern for most cases is to call deref() on the pointer to get a Java object + * (that you can keep as long as you like), and then immediately destroy the + * struct if it needs destroying. To find out whether and how the struct needs + * to be destroyed, see the library's documentation of the function that you got + * the struct from. * * To pass new structs to the library, you can use the static createJavaOwned() * function in each PointerType, which creates a new struct from the Java object @@ -146,13 +143,13 @@ } public static class RoomArrayPtr extends PointerType { - public RoomlistRoom[] getRooms(int count) { + public Room[] getRooms(int count) { Pointer ptr = getPointer(); if(ptr == null) { - return new RoomlistRoom[0]; + return new Room[0]; } Pointer[] untypedPtrs = ptr.getPointerArray(0, count); - RoomlistRoom[] result = new RoomlistRoom[count]; + Room[] result = new Room[count]; for(int i=0; i<count; i++) { result[i] = RoomPtr.deref(untypedPtrs[i]); } @@ -161,11 +158,11 @@ } public static class RoomPtr extends PointerType { - public RoomlistRoom deref() { + public Room deref() { return deref(getPointer()); } - public static RoomlistRoom deref(Pointer p) { + public static Room deref(Pointer p) { RoomStruct struct = new RoomStruct(p); struct.read(); return struct.toRoomlistRoom(); @@ -420,13 +417,12 @@ static class WeaponsetStruct extends Structure { public static class ByVal extends WeaponsetStruct implements Structure.ByValue {} public static class ByRef extends WeaponsetStruct implements Structure.ByReference {} - private static String[] FIELD_ORDER = new String[] {"_referenceCount", "loadout", "crateprob", "crateammo", "delay", "name"}; + private static String[] FIELD_ORDER = new String[] {"loadout", "crateprob", "crateammo", "delay", "name"}; public WeaponsetStruct() { super(); setFieldOrder(FIELD_ORDER); } public WeaponsetStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); } public void fillFrom(Weaponset weaponset) { - _referenceCount = 0; fillWeaponInfo(loadout, weaponset.loadout); fillWeaponInfo(crateprob, weaponset.crateProb); fillWeaponInfo(crateammo, weaponset.crateAmmo); @@ -453,7 +449,6 @@ } } - public int _referenceCount; public byte[] loadout = new byte[Weaponset.WEAPONS_COUNT+1]; public byte[] crateprob = new byte[Weaponset.WEAPONS_COUNT+1]; public byte[] crateammo = new byte[Weaponset.WEAPONS_COUNT+1]; @@ -483,13 +478,17 @@ public void fillFrom(List<Weaponset> list) { weaponsetCount = list.size(); - weaponsets = new WeaponsetPointerByReference(); - Structure[] structs = weaponsets.toArray(weaponsetCount); - - for(int i=0; i<weaponsetCount; i++) { - WeaponsetPointerByReference pstruct = (WeaponsetPointerByReference)structs[i]; - pstruct.weaponset = new WeaponsetStruct.ByRef(); - pstruct.weaponset.fillFrom(list.get(i)); + if(weaponsetCount<=0) { + weaponsets = null; + } else { + weaponsets = new WeaponsetPointerByReference(); + Structure[] structs = weaponsets.toArray(weaponsetCount); + + for(int i=0; i<weaponsetCount; i++) { + WeaponsetPointerByReference pstruct = (WeaponsetPointerByReference)structs[i]; + pstruct.weaponset = new WeaponsetStruct.ByRef(); + pstruct.weaponset.fillFrom(list.get(i)); + } } } @@ -525,8 +524,8 @@ public RoomStruct() { super(); setFieldOrder(FIELD_ORDER); } public RoomStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); } - public RoomlistRoom toRoomlistRoom() { - return new RoomlistRoom(name, map, scheme, weapons, owner, playerCount, teamCount, inProgress); + public Room toRoomlistRoom() { + return new Room(name, map, scheme, weapons, owner, playerCount, teamCount, inProgress); } public boolean inProgress; @@ -641,31 +640,11 @@ public static class ByVal extends MetaschemeStruct implements Structure.ByValue {} public static class ByRef extends MetaschemeStruct implements Structure.ByReference {} - private static String[] FIELD_ORDER = new String[] {"_referenceCount", "settingCount", "modCount", "settings", "mods"}; + private static String[] FIELD_ORDER = new String[] {"settingCount", "modCount", "settings", "mods"}; public MetaschemeStruct() { super(); setFieldOrder(FIELD_ORDER); } public MetaschemeStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); } - public void fillFrom(MetaScheme metascheme) { - settingCount = metascheme.settings.size(); - modCount = metascheme.mods.size(); - - settings = new MetaschemeSettingStruct.ByRef(); - Structure[] settingStructs = settings.toArray(settingCount); - mods = new MetaschemeModStruct.ByRef(); - Structure[] modStructs = mods.toArray(modCount); - - for(int i=0; i<settingCount; i++) { - MetaschemeSettingStruct mss = (MetaschemeSettingStruct)settingStructs[i]; - mss.fillFrom(metascheme.settings.get(i)); - } - - for(int i=0; i<modCount; i++) { - MetaschemeModStruct mms = (MetaschemeModStruct)modStructs[i]; - mms.fillFrom(metascheme.mods.get(i)); - } - } - /** * Only use on native-owned structs! * Calling this method on a Java-owned struct could cause garbage collection of referenced @@ -691,7 +670,6 @@ return new MetaScheme(modList, settingList); } - public int _referenceCount; public int settingCount; public int modCount; public MetaschemeSettingStruct.ByRef settings; @@ -701,41 +679,39 @@ static class SchemeStruct extends Structure { public static class ByVal extends SchemeStruct implements Structure.ByValue {} public static class ByRef extends SchemeStruct implements Structure.ByReference {} - private static String[] FIELD_ORDER = new String[] {"meta", "name", "settings", "mod"}; + private static String[] FIELD_ORDER = new String[] {"name", "settings", "mod"}; public SchemeStruct() { super(); setFieldOrder(FIELD_ORDER); } public SchemeStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); } public void fillFrom(Scheme scheme) { - meta = new MetaschemeStruct.ByRef(); - meta.fillFrom(scheme.metascheme); + MetaScheme meta = MetaScheme.INSTANCE; name = scheme.name; - settings = new Memory(NATIVE_INT_SIZE * scheme.metascheme.settings.size()); - for(int i=0; i<scheme.metascheme.settings.size(); i++) { - Integer value = scheme.settings.get(scheme.metascheme.settings.get(i).name); + settings = new Memory(NATIVE_INT_SIZE * meta.settings.size()); + for(int i=0; i<meta.settings.size(); i++) { + Integer value = scheme.settings.get(meta.settings.get(i).name); settings.setInt(NATIVE_INT_SIZE*i, value); } - mods = new Memory(NATIVE_BOOL_SIZE * scheme.metascheme.mods.size()); - for(int i=0; i<scheme.metascheme.mods.size(); i++) { - Boolean value = scheme.mods.get(scheme.metascheme.mods.get(i).name); + mods = new Memory(NATIVE_BOOL_SIZE * meta.mods.size()); + for(int i=0; i<meta.mods.size(); i++) { + Boolean value = scheme.mods.get(meta.mods.get(i).name); mods.setByte(NATIVE_BOOL_SIZE*i, (byte)(value ? 1 : 0)); } } public Scheme toScheme() { - MetaScheme metaScheme = meta.toMetaScheme(); Map<String, Integer> settingsMap = new HashMap<String, Integer>(); - for(int i=0; i<metaScheme.settings.size(); i++) { - settingsMap.put(metaScheme.settings.get(i).name, settings.getInt(NATIVE_INT_SIZE*i)); + MetaScheme meta = MetaScheme.INSTANCE; + for(int i=0; i<meta.settings.size(); i++) { + settingsMap.put(meta.settings.get(i).name, settings.getInt(NATIVE_INT_SIZE*i)); } Map<String, Boolean> modsMap = new HashMap<String, Boolean>(); - for(int i=0; i<metaScheme.mods.size(); i++) { - modsMap.put(metaScheme.mods.get(i).name, mods.getByte(i) != 0 ? Boolean.TRUE : Boolean.FALSE); + for(int i=0; i<meta.mods.size(); i++) { + modsMap.put(meta.mods.get(i).name, mods.getByte(i) != 0 ? Boolean.TRUE : Boolean.FALSE); } - return new Scheme(metaScheme, name, settingsMap, modsMap); + return new Scheme(name, settingsMap, modsMap); } - public MetaschemeStruct.ByRef meta; public String name; public Pointer settings; public Pointer mods; @@ -763,13 +739,17 @@ public void fillFrom(List<Scheme> schemeList) { schemeCount = schemeList.size(); - schemes = new SchemePointerByReference(); - Structure[] schemePtrStructs = schemes.toArray(schemeCount); - - for(int i=0; i<this.schemeCount; i++) { - SchemePointerByReference spbr = (SchemePointerByReference)schemePtrStructs[i]; - spbr.scheme = new SchemeStruct.ByRef(); - spbr.scheme.fillFrom(schemeList.get(i)); + if(schemeCount<=0) { + schemes = null; + } else { + schemes = new SchemePointerByReference(); + Structure[] schemePtrStructs = schemes.toArray(schemeCount); + + for(int i=0; i<this.schemeCount; i++) { + SchemePointerByReference spbr = (SchemePointerByReference)schemePtrStructs[i]; + spbr.scheme = new SchemeStruct.ByRef(); + spbr.scheme.fillFrom(schemeList.get(i)); + } } } @@ -821,13 +801,17 @@ public void fillFrom(List<TeamInGame> teamList, WeaponsetStruct.ByRef weaponset, int initialHealth) { teamCount = teamList.size(); - teams = new TeamPointerByReference(); - Structure[] teamPtrStructs = teams.toArray(teamCount); - - for(int i=0; i<this.teamCount; i++) { - TeamPointerByReference tpbr = (TeamPointerByReference)teamPtrStructs[i]; - tpbr.team = new TeamStruct.ByRef(); - tpbr.team.fillFrom(teamList.get(i), weaponset, initialHealth); + if(teamCount <= 0) { + teams = null; + } else { + teams = new TeamPointerByReference(); + Structure[] teamPtrStructs = teams.toArray(teamCount); + + for(int i=0; i<this.teamCount; i++) { + TeamPointerByReference tpbr = (TeamPointerByReference)teamPtrStructs[i]; + tpbr.team = new TeamStruct.ByRef(); + tpbr.team.fillFrom(teamList.get(i), weaponset, initialHealth); + } } } @@ -986,7 +970,8 @@ int flib_get_teamcolor_count(); int flib_get_hedgehogs_per_team(); int flib_get_weapons_count(); - + MetaschemePtr flib_get_metascheme(); + // net/netconn.h static final int NETCONN_STATE_CONNECTING = 0; static final int NETCONN_STATE_LOBBY = 1; @@ -1016,7 +1001,7 @@ static final int NETCONN_MAPCHANGE_THEME = 6; static final int NETCONN_MAPCHANGE_SEED = 7; - NetconnPtr flib_netconn_create(String playerName, MetaschemePtr meta, String dataDirPath, String host, int port); + NetconnPtr flib_netconn_create(String playerName, String dataDirPath, String host, int port); void flib_netconn_destroy(NetconnPtr conn); void flib_netconn_tick(NetconnPtr conn); @@ -1036,7 +1021,7 @@ int flib_netconn_send_toggleReady(NetconnPtr conn); int flib_netconn_send_addTeam(NetconnPtr conn, TeamPtr team); int flib_netconn_send_removeTeam(NetconnPtr conn, String teamname); - int flib_netconn_send_engineMessage(NetconnPtr conn, Buffer message, NativeLong size); + int flib_netconn_send_engineMessage(NetconnPtr conn, Pointer message, NativeLong size); int flib_netconn_send_teamHogCount(NetconnPtr conn, String teamname, int hogcount); int flib_netconn_send_teamColor(NetconnPtr conn, String teamname, int colorIndex); int flib_netconn_send_weaponset(NetconnPtr conn, WeaponsetPtr weaponset); @@ -1047,7 +1032,7 @@ int flib_netconn_send_mapMazeSize(NetconnPtr conn, int mazeSize); int flib_netconn_send_mapSeed(NetconnPtr conn, String seed); int flib_netconn_send_mapTheme(NetconnPtr conn, String theme); - int flib_netconn_send_mapDrawdata(NetconnPtr conn, Buffer drawData, NativeLong size); + int flib_netconn_send_mapDrawdata(NetconnPtr conn, Pointer drawData, NativeLong size); int flib_netconn_send_script(NetconnPtr conn, String scriptName); int flib_netconn_send_scheme(NetconnPtr conn, SchemePtr scheme); int flib_netconn_send_roundfinished(NetconnPtr conn, boolean withoutError); @@ -1109,7 +1094,7 @@ int flib_gameconn_getport(GameconnPtr conn); void flib_gameconn_tick(GameconnPtr conn); - int flib_gameconn_send_enginemsg(GameconnPtr conn, Buffer data, NativeLong len); + int flib_gameconn_send_enginemsg(GameconnPtr conn, Pointer data, NativeLong len); int flib_gameconn_send_textmsg(GameconnPtr conn, int msgtype, String msg); int flib_gameconn_send_chatmsg(GameconnPtr conn, String playername, String msg); int flib_gameconn_send_quit(GameconnPtr conn); @@ -1122,6 +1107,10 @@ void flib_gameconn_onEngineMessage(GameconnPtr conn, BytesCallback callback, Pointer context); // ipc/mapconn.h + public static final int MAPIMAGE_WIDTH = 256; + public static final int MAPIMAGE_HEIGHT = 128; + public static final int MAPIMAGE_BYTES = (MAPIMAGE_WIDTH/8*MAPIMAGE_HEIGHT); + MapconnPtr flib_mapconn_create(MapRecipePtr mapdesc); void flib_mapconn_destroy(MapconnPtr conn); int flib_mapconn_getport(MapconnPtr conn); @@ -1149,13 +1138,8 @@ public static final int MAZE_SIZE_MEDIUM_ISLANDS = 4; public static final int MAZE_SIZE_LARGE_ISLANDS = 5; - // model/scheme.h - MetaschemePtr flib_metascheme_from_ini(String filename); - MetaschemePtr flib_metascheme_retain(MetaschemePtr metainfo); - void flib_metascheme_release(MetaschemePtr metainfo); - // model/schemelist.h - SchemelistPtr flib_schemelist_from_ini(MetaschemePtr meta, String filename); + SchemelistPtr flib_schemelist_from_ini(String filename); int flib_schemelist_to_ini(String filename, SchemelistPtr list); void flib_schemelist_destroy(SchemelistPtr list); @@ -1169,6 +1153,9 @@ int flib_weaponsetlist_to_ini(String filename, WeaponsetListPtr weaponsets); void flib_weaponsetlist_destroy(WeaponsetListPtr list); + // model/gamesetup.h + void flib_gamesetup_destroy(GameSetupPtr gamesetup); + // util/logging.h public static final int FLIB_LOGLEVEL_ALL = -100; public static final int FLIB_LOGLEVEL_DEBUG = -1; diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ChatFragment.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ChatFragment.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import org.hedgewars.hedgeroid.R; - -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; - -public class ChatFragment extends Fragment { - public static final String ARGUMENT_INROOM = "inRoom"; - - private ChatlogAdapter adapter; - private Netplay netconn; - private MessageLog messageLog; - private boolean inRoom; - - public void setInRoom(boolean inRoom) { - this.inRoom = inRoom; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - netconn = Netplay.getAppInstance(getActivity().getApplicationContext()); - adapter = new ChatlogAdapter(getActivity()); - } - - @Override - public void onStart() { - super.onStart(); - messageLog = inRoom ? netconn.roomChatlog : netconn.lobbyChatlog; - adapter.setLog(messageLog.getLog()); - messageLog.registerObserver(adapter); - } - - @Override - public void onStop() { - super.onStop(); - messageLog.unregisterObserver(adapter); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_chat, container, false); - - ListView listView = (ListView) view.findViewById(R.id.chatConsole); - listView.setAdapter(adapter); - listView.setDivider(null); - listView.setDividerHeight(0); - listView.setVerticalFadingEdgeEnabled(true); - - EditText editText = (EditText) view.findViewById(R.id.chatInput); - editText.setOnEditorActionListener(new ChatSendListener()); - - return view; - } - - private final class ChatSendListener implements OnEditorActionListener { - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - String text = v.getText().toString(); - if(text.length()>0) { - v.setText(""); - netconn.sendChat(text); - } - return true; - } - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ChatlogAdapter.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ChatlogAdapter.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,95 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.hedgewars.hedgeroid.netplay.MessageLog.Observer; - -import android.content.Context; -import android.text.method.LinkMovementMethod; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView.LayoutParams; -import android.widget.BaseAdapter; -import android.widget.TextView; - -/** - * Optimization: ListView is smart enough to try re-using the same view for an item - * with the same ID, but it still calls getView for those items when the list changes. - * Since lines with a given ID never change in our chatlog, we can avoid the effort - * of TextView.setText in many cases by checking if the view is already set up for the - * line with the right ID - but to do that, the view needs to remember the ID it's - * holding the text for. That's what the LoglineView does. - */ -class LoglineView extends TextView { - long chatlogId = -1; - - public LoglineView(Context context) { - super(context); - } -} - -public class ChatlogAdapter extends BaseAdapter implements Observer { - long idOffset = 0; - private List<CharSequence> log = new ArrayList<CharSequence>(); - private Context context; - - public ChatlogAdapter(Context context) { - this.context = context; - } - - public int getCount() { - return log.size(); - } - - public Object getItem(int position) { - return log.get(position); - } - - public long getItemId(int position) { - return position+idOffset; - } - - public boolean hasStableIds() { - return true; - } - - public void clear() { - idOffset += log.size(); - log.clear(); - notifyDataSetChanged(); - } - - public void lineAdded(CharSequence text) { - log.add(text); - notifyDataSetChanged(); - } - - public void lineRemoved() { - log.remove(0); - idOffset += 1; - notifyDataSetChanged(); - } - - public void setLog(Collection<CharSequence> log) { - idOffset += log.size(); - this.log = new ArrayList<CharSequence>(log); - notifyDataSetChanged(); - } - - public View getView(int position, View convertView, ViewGroup parent) { - LoglineView v = (LoglineView)convertView; - if (v == null) { - v = new LoglineView(context); - v.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); - v.setMovementMethod(LinkMovementMethod.getInstance()); - } - long id = getItemId(position); - if(id != v.chatlogId) { - v.setText(log.get(position)); - v.chatlogId = id; - } - return v; - } -} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/GameMessageListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/GameMessageListener.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,15 @@ +package org.hedgewars.hedgeroid.netplay; + +/** + * Interface with several event callbacks that represent network messages which are interesting + * for a running game, e.g. because they concern the lifecycle of the game or because they contain + * data that needs to be passed on. + * + * These functions might be called on any thread. + */ +public interface GameMessageListener { + void onChatMessage(String nick, String message); + void onEngineMessage(byte[] em); + void onMessage(int type, String message); + void onNetDisconnected(); +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/LobbyActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/LobbyActivity.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.netplay.Netplay.State; -import org.hedgewars.hedgeroid.netplay.NetplayStateFragment.NetplayStateListener; -import org.hedgewars.hedgeroid.netplay.TextInputDialog.TextInputDialogListener; - -import android.content.Context; -import android.content.Intent; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentTransaction; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TabHost; -import android.widget.TextView; - -public class LobbyActivity extends FragmentActivity implements TextInputDialogListener, NetplayStateListener { - private static final int DIALOG_CREATE_ROOM = 0; - - private TabHost tabHost; - private Netplay netplay; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - setContentView(R.layout.activity_lobby); - ChatFragment chatFragment = (ChatFragment)getSupportFragmentManager().findFragmentById(R.id.chatFragment); - chatFragment.setInRoom(false); - - FragmentTransaction trans = getSupportFragmentManager().beginTransaction(); - trans.add(new NetplayStateFragment(), "netplayFragment"); - trans.commit(); - - netplay = Netplay.getAppInstance(getApplicationContext()); - - tabHost = (TabHost)findViewById(android.R.id.tabhost); - if(tabHost != null) { - tabHost.setup(); - tabHost.getTabWidget().setOrientation(LinearLayout.VERTICAL); - - tabHost.addTab(tabHost.newTabSpec("rooms").setIndicator(createIndicatorView(tabHost, R.string.lobby_tab_rooms, getResources().getDrawable(R.drawable.roomlist_ingame))).setContent(R.id.roomListFragment)); - tabHost.addTab(tabHost.newTabSpec("chat").setIndicator(createIndicatorView(tabHost, R.string.lobby_tab_chat, getResources().getDrawable(R.drawable.edit))).setContent(R.id.chatFragment)); - tabHost.addTab(tabHost.newTabSpec("players").setIndicator(createIndicatorView(tabHost, R.string.lobby_tab_players, getResources().getDrawable(R.drawable.human))).setContent(R.id.playerListFragment)); - - if (icicle != null) { - tabHost.setCurrentTabByTag(icicle.getString("currentTab")); - } - } - } - - private View createIndicatorView(TabHost tabHost, int label, Drawable icon) { - LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - View tabIndicator = inflater.inflate(R.layout.tab_indicator, - tabHost.getTabWidget(), // tab widget is the parent - false); // no inflate params - - final TextView tv = (TextView) tabIndicator.findViewById(R.id.title); - tv.setText(label); - - if(icon != null) { - final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon); - iconView.setImageDrawable(icon); - } - - return tabIndicator; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.lobby_options, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch(item.getItemId()) { - case R.id.room_create: - TextInputDialog dialog = new TextInputDialog(DIALOG_CREATE_ROOM, R.string.dialog_create_room_title, 0, R.string.dialog_create_room_hint); - dialog.show(getSupportFragmentManager(), "create_room_dialog"); - return true; - case R.id.disconnect: - netplay.disconnect(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onBackPressed() { - super.onBackPressed(); - netplay.disconnect(); - } - - @Override - protected void onSaveInstanceState(Bundle icicle) { - super.onSaveInstanceState(icicle); - if(tabHost != null) { - icicle.putString("currentTab", tabHost.getCurrentTabTag()); - } - } - - public void onTextInputDialogSubmitted(int dialogId, String text) { - if(text != null && text.length()>0) { - netplay.sendCreateRoom(text); - } - } - - public void onTextInputDialogCancelled(int dialogId) { - } - - public void onNetplayStateChanged(State newState) { - switch(newState) { - case CONNECTING: - case NOT_CONNECTED: - finish(); - break; - case ROOM: - case INGAME: - startActivity(new Intent(getApplicationContext(), RoomActivity.class)); - break; - case LOBBY: - // Do nothing - break; - default: - throw new IllegalStateException("Unknown connection state: "+newState); - } - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/LobbyPlayerlist.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/LobbyPlayerlist.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import org.hedgewars.hedgeroid.Datastructures.Player; - -import android.util.Pair; - -public class LobbyPlayerlist extends ObservableTreeMap<String, Pair<Player, Long>> { - private long nextId = 1; - - public void addPlayerWithNewId(String name) { - put(name, Pair.create(new Player(name), nextId++)); - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/LobbyPlayerlistAdapter.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/LobbyPlayerlistAdapter.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import java.util.Comparator; - -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.Datastructures.Player; - -import android.content.Context; -import android.util.Pair; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -public class LobbyPlayerlistAdapter extends ObservableTreeMapAdapter<String, Pair<Player, Long>> { - private Context context; - - public LobbyPlayerlistAdapter(Context context) { - this.context = context; - } - - @Override - protected Comparator<Pair<Player, Long>> getEntryOrder() { - return AlphabeticalOrderComparator.INSTANCE; - } - - public Player getItem(int position) { - return getEntries().get(position).first; - } - - public long getItemId(int position) { - return getEntries().get(position).second; - } - - public boolean hasStableIds() { - return true; - } - - public View getView(int position, View convertView, ViewGroup parent) { - View v = convertView; - if (v == null) { - LayoutInflater vi = LayoutInflater.from(context); - v = vi.inflate(R.layout.listview_player, null); - } - - String player = getItem(position).name; - TextView username = (TextView) v.findViewById(android.R.id.text1); - username.setText(player); - return v; - } - - private static final class AlphabeticalOrderComparator implements Comparator<Pair<Player, Long>> { - public static final AlphabeticalOrderComparator INSTANCE = new AlphabeticalOrderComparator(); - public int compare(Pair<Player, Long> lhs, Pair<Player, Long> rhs) { - return lhs.first.name.compareToIgnoreCase(rhs.first.name); - }; - } -} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/LobbyPlayerlistFragment.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/LobbyPlayerlistFragment.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.Datastructures.Player; - -import android.os.Bundle; -import android.support.v4.app.ListFragment; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.LayoutInflater; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView.AdapterContextMenuInfo; - -public class LobbyPlayerlistFragment extends ListFragment { - private Netplay netplay; - private LobbyPlayerlistAdapter adapter; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - netplay = Netplay.getAppInstance(getActivity().getApplicationContext()); - adapter = new LobbyPlayerlistAdapter(getActivity()); - adapter.setSource(netplay.lobbyPlayerlist); - setListAdapter(adapter); - } - - @Override - public void onDestroy() { - super.onDestroy(); - adapter.invalidate(); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - registerForContextMenu(getListView()); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo; - MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.lobby_playerlist_context, menu); - menu.setHeaderIcon(R.drawable.human); - menu.setHeaderTitle(adapter.getItem(info.position).name); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo(); - Player player = adapter.getItem(info.position); - switch(item.getItemId()) { - case R.id.player_info: - netplay.sendPlayerInfoQuery(player.name); - return true; - case R.id.player_follow: - netplay.sendFollowPlayer(player.name); - return true; - default: - return super.onContextItemSelected(item); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_playerlist, container, false); - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/NetRoomState.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/NetRoomState.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,207 @@ +package org.hedgewars.hedgeroid.netplay; + +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.MSG_SEND_GAMESTYLE; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.MSG_SEND_MAP; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.MSG_SEND_MAP_DRAWDATA; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.MSG_SEND_MAP_GENERATOR; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.MSG_SEND_MAP_NAME; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.MSG_SEND_MAP_SEED; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.MSG_SEND_MAP_TEMPLATE; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.MSG_SEND_MAP_THEME; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.MSG_SEND_MAZE_SIZE; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.MSG_SEND_SCHEME; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.MSG_SEND_WEAPONSET; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import org.hedgewars.hedgeroid.RoomStateManager; +import org.hedgewars.hedgeroid.Datastructures.GameConfig; +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; +import org.hedgewars.hedgeroid.Datastructures.Scheme; +import org.hedgewars.hedgeroid.Datastructures.Weaponset; +import org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType; + +/** + * This class manages the room state in a network game. + */ +class NetRoomState implements RoomStateManager { + private List<RoomStateManager.Observer> observers = new LinkedList<RoomStateManager.Observer>(); + private Netplay netplay; + + boolean chief; + String gameStyle; + Scheme scheme; + MapRecipe map; + Weaponset weaponset; + + public NetRoomState(Netplay netplay) { + this.netplay = netplay; + this.map = MapRecipe.makeRandomMap(0, "seed", GameConfig.DEFAULT_THEME); + } + + public MapRecipe getMapRecipe() { + return map; + } + + public boolean getChiefStatus() { + return chief; + } + + public Scheme getScheme() { + return scheme; + } + + public String getGameStyle() { + return gameStyle; + } + + public Weaponset getWeaponset() { + return weaponset; + } + + public void changeWeaponset(Weaponset weaponset) { + if(chief && !weaponset.equals(this.weaponset)) { + sendToNet(MSG_SEND_WEAPONSET, weaponset); + setWeaponset(weaponset); + } + } + + public void changeMapRecipe(MapRecipe mapRecipe) { + if(chief && !mapRecipe.equals(this.map)) { + sendToNet(MSG_SEND_MAP, mapRecipe); + setMapRecipe(mapRecipe); + } + } + + public void changeMapNameAndGenerator(String mapName) { + if(chief && !mapName.equals(this.map.name)) { + int newGenerator = MapRecipe.generatorForMapname(mapName); + if(newGenerator != this.map.mapgen) { + sendToNet(MSG_SEND_MAP_GENERATOR, newGenerator, null); + } + sendToNet(MSG_SEND_MAP_NAME, mapName); + setMapRecipe(map.withName(mapName).withMapgen(newGenerator)); + } + } + + public void changeMapTemplate(int template) { + if(chief && template != this.map.templateFilter) { + sendToNet(MSG_SEND_MAP_TEMPLATE, template, null); + setMapRecipe(map.withTemplateFilter(template)); + } + } + + public void changeMazeSize(int mazeSize) { + if(chief && mazeSize != this.map.mazeSize) { + sendToNet(MSG_SEND_MAZE_SIZE, mazeSize, 0); + setMapRecipe(map.withMazeSize(mazeSize)); + } + } + + public void changeMapSeed(String seed) { + if(chief && !seed.equals(this.map.seed)) { + sendToNet(MSG_SEND_MAP_SEED, seed); + setMapRecipe(map.withSeed(seed)); + } + } + + public void changeMapTheme(String theme) { + if(chief && !theme.equals(this.map.theme)) { + sendToNet(MSG_SEND_MAP_THEME, theme); + setMapRecipe(map.withTheme(theme)); + } + } + + public void changeMapDrawdata(byte[] drawdata) { + if(chief && !Arrays.equals(drawdata, this.map.getDrawData())) { + sendToNet(MSG_SEND_MAP_DRAWDATA, drawdata); + setMapRecipe(map.withDrawData(drawdata)); + } + } + + public void changeGameStyle(String gameStyle) { + if(chief && !gameStyle.equals(this.gameStyle)) { + sendToNet(MSG_SEND_GAMESTYLE, gameStyle); + setGameStyle(gameStyle); + } + } + + public void changeScheme(Scheme scheme) { + if(chief && !scheme.equals(this.scheme)) { + sendToNet(MSG_SEND_SCHEME, scheme); + setScheme(scheme); + } + } + + void setWeaponset(Weaponset weaponset) { + if(!weaponset.equals(this.weaponset)) { + this.weaponset = weaponset; + for(RoomStateManager.Observer observer : observers) { + observer.onWeaponsetChanged(weaponset); + } + } + } + + void setMapRecipe(MapRecipe map) { + if(!map.equals(this.map)) { + this.map = map; + for(RoomStateManager.Observer observer : observers) { + observer.onMapChanged(map); + } + } + } + + void setGameStyle(String gameStyle) { + if(!gameStyle.equals(this.gameStyle)) { + this.gameStyle = gameStyle; + for(RoomStateManager.Observer observer : observers) { + observer.onGameStyleChanged(gameStyle); + } + } + } + + void setScheme(Scheme scheme) { + if(!scheme.equals(this.scheme)) { + this.scheme = scheme; + for(RoomStateManager.Observer observer : observers) { + observer.onSchemeChanged(scheme); + } + } + } + + void setChief(boolean chief) { + if(chief != this.chief) { + this.chief = chief; + for(RoomStateManager.Observer observer : observers) { + observer.onChiefStatusChanged(chief); + } + } + } + + void sendFullConfig() { + if(chief) { + sendToNet(MSG_SEND_GAMESTYLE, gameStyle); + sendToNet(MSG_SEND_SCHEME, scheme); + sendToNet(MSG_SEND_WEAPONSET, weaponset); + sendToNet(MSG_SEND_MAP, map); + } + } + + public void registerObserver(Observer observer) { + observers.add(observer); + } + + public void unregisterObserver(Observer observer) { + observers.remove(observer); + } + + private boolean sendToNet(ToNetMsgType what, Object obj) { + return netplay.sendToNet(what, 0, obj); + } + + private boolean sendToNet(ToNetMsgType what, int arg1, Object obj) { + return netplay.sendToNet(what, arg1, obj); + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java Sat Aug 18 00:47:51 2012 +0200 @@ -1,48 +1,42 @@ package org.hedgewars.hedgeroid.netplay; -import java.io.File; -import java.io.FileNotFoundException; +import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.*; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.TreeMap; -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.Utils; -import org.hedgewars.hedgeroid.Datastructures.RoomlistRoom; +import org.hedgewars.hedgeroid.RoomStateManager; +import org.hedgewars.hedgeroid.Datastructures.GameConfig; +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; +import org.hedgewars.hedgeroid.Datastructures.Player; +import org.hedgewars.hedgeroid.Datastructures.PlayerInRoom; +import org.hedgewars.hedgeroid.Datastructures.Room; +import org.hedgewars.hedgeroid.Datastructures.Scheme; +import org.hedgewars.hedgeroid.Datastructures.Schemes; import org.hedgewars.hedgeroid.Datastructures.Team; import org.hedgewars.hedgeroid.Datastructures.TeamInGame; import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes; +import org.hedgewars.hedgeroid.Datastructures.Weaponset; +import org.hedgewars.hedgeroid.Datastructures.Weaponsets; import org.hedgewars.hedgeroid.frontlib.Flib; -import org.hedgewars.hedgeroid.frontlib.Frontlib; -import org.hedgewars.hedgeroid.frontlib.Frontlib.BoolCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.IntStrCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.MetaschemePtr; -import org.hedgewars.hedgeroid.frontlib.Frontlib.NetconnPtr; -import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomArrayPtr; -import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomListCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomPtr; -import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.StrIntCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.StrRoomCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.StrStrCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamCallback; -import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr; -import org.hedgewars.hedgeroid.frontlib.Frontlib.VoidCallback; +import org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType; +import org.hedgewars.hedgeroid.util.ObservableTreeMap; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.content.res.Resources; import android.os.Handler; -import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.util.Pair; -import com.sun.jna.Pointer; /** * This class manages the application's networking state. @@ -72,21 +66,25 @@ private final FromNetHandler fromNetHandler = new FromNetHandler(); private State state = State.NOT_CONNECTED; - private int foregroundUsers = 0; // Reference counter of clients requesting foreground tick speed (fast ticks) - private boolean chief; // Do we control the current room? private String playerName; + // null or stale if not in room state + private final NetRoomState netRoomState = new NetRoomState(this); + // null if there is no running connection (==state is NOT_CONNECTED) private ThreadedNetConnection connection; - public final LobbyPlayerlist lobbyPlayerlist = new LobbyPlayerlist(); - public final RoomPlayerlist roomPlayerlist = new RoomPlayerlist(); + public final ObservableTreeMap<String, Player> lobbyPlayerlist = new ObservableTreeMap<String, Player>(); + public final ObservableTreeMap<String, PlayerInRoom> roomPlayerlist = new ObservableTreeMap<String, PlayerInRoom>(); public final Roomlist roomList = new Roomlist(); public final MessageLog lobbyChatlog; public final MessageLog roomChatlog; - public final Teamlist roomTeamlist = new Teamlist(); + public final ObservableTreeMap<String, TeamInGame> roomTeamlist = new ObservableTreeMap<String, TeamInGame>(); private final Map<String, Team> roomRequestedTeams = new TreeMap<String, Team>(); + private final List<GameMessageListener> gameMessageListeners = new LinkedList<GameMessageListener>(); + private final List<RunGameListener> runGameListeners = new LinkedList<RunGameListener>(); + public Netplay(Context appContext) { this.appContext = appContext; broadcastManager = LocalBroadcastManager.getInstance(appContext); @@ -94,12 +92,52 @@ roomChatlog = new MessageLog(appContext); } - private void clearState() { + public RoomStateManager getRoomStateManager() { + return netRoomState; + } + + private void clearLobbyState() { lobbyPlayerlist.clear(); roomList.clear(); lobbyChatlog.clear(); } + private void initRoomState(boolean chief) { + roomChatlog.clear(); + roomPlayerlist.clear(); + roomTeamlist.clear(); + roomRequestedTeams.clear(); + + try { + netRoomState.setChief(chief); + netRoomState.setGameStyle(GameConfig.DEFAULT_STYLE); + List<Scheme> schemes = Schemes.loadBuiltinSchemes(appContext); + netRoomState.setScheme(schemes.get(Schemes.toNameList(schemes).indexOf(GameConfig.DEFAULT_SCHEME))); + netRoomState.setMapRecipe(MapRecipe.makeRandomMap(0, MapRecipe.makeRandomSeed(), GameConfig.DEFAULT_THEME)); + List<Weaponset> weaponsets = Weaponsets.loadBuiltinWeaponsets(appContext); + netRoomState.setWeaponset(weaponsets.get(Weaponsets.toNameList(weaponsets).indexOf(GameConfig.DEFAULT_WEAPONSET))); + netRoomState.sendFullConfig(); + } catch(IOException e) { + throw new RuntimeException(e); + } + } + + public void registerGameMessageListener(GameMessageListener listener) { + gameMessageListeners.add(listener); + } + + public void unregisterGameMessageListener(GameMessageListener listener) { + gameMessageListeners.remove(listener); + } + + public void registerRunGameListener(RunGameListener listener) { + runGameListeners.add(listener); + } + + public void unregisterRunGameListener(RunGameListener listener) { + runGameListeners.remove(listener); + } + public void connectToDefaultServer(String playerName) { connect(playerName, DEFAULT_SERVER, DEFAULT_PORT); } @@ -117,35 +155,39 @@ throw new IllegalStateException("Attempt to start a new connection while the old one was still running."); } - clearState(); + clearLobbyState(); changeState(State.CONNECTING); connection = ThreadedNetConnection.startConnection(appContext, fromNetHandler, name, host, port); - connection.setFastTickRate(foregroundUsers > 0); + connection.setFastTickRate(true); } public void sendNick(String nick) { playerName = nick; - sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_NICK, nick); + sendToNet(MSG_SEND_NICK, nick); } - public void sendPassword(String password) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PASSWORD, password); } - public void sendQuit(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_QUIT, message); } - public void sendRoomlistRequest() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_ROOMLIST_REQUEST); } - public void sendPlayerInfoQuery(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_PLAYER_INFO_REQUEST, name); } - public void sendChat(String s) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CHAT, s); } - public void sendFollowPlayer(String nick) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_FOLLOW_PLAYER, nick); } - public void sendJoinRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_JOIN_ROOM, name); } - public void sendCreateRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CREATE_ROOM, name); } - public void sendLeaveRoom(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_LEAVE_ROOM, message); } - public void sendKick(String player) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_KICK, player); } + public void sendPassword(String password) { sendToNet(MSG_SEND_PASSWORD, password); } + public void sendQuit(String message) { sendToNet(MSG_SEND_QUIT, message); } + public void sendRoomlistRequest() { sendToNet(MSG_SEND_ROOMLIST_REQUEST); } + public void sendPlayerInfoQuery(String name) { sendToNet(MSG_SEND_PLAYER_INFO_REQUEST, name); } + public void sendChat(String s) { sendToNet(MSG_SEND_CHAT, s); } + public void sendTeamChat(String s) { sendToNet(MSG_SEND_TEAMCHAT, s); } + public void sendFollowPlayer(String nick) { sendToNet(MSG_SEND_FOLLOW_PLAYER, nick); } + public void sendJoinRoom(String name) { sendToNet(MSG_SEND_JOIN_ROOM, name); } + public void sendCreateRoom(String name) { sendToNet(MSG_SEND_CREATE_ROOM, name); } + public void sendLeaveRoom(String message) { sendToNet(MSG_SEND_LEAVE_ROOM, message); } + public void sendKick(String player) { sendToNet(MSG_SEND_KICK, player); } public void sendAddTeam(Team newTeam) { roomRequestedTeams.put(newTeam.name, newTeam); - sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_ADD_TEAM, newTeam); + sendToNet(MSG_SEND_ADD_TEAM, newTeam); } - public void sendRemoveTeam(String teamName) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_REMOVE_TEAM, teamName); } - public void sendTeamColorIndex(String teamName, int colorIndex) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_TEAM_COLOR_INDEX, colorIndex, teamName); } - public void sendTeamHogCount(String teamName, int hogCount) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_TEAM_HOG_COUNT, hogCount, teamName); } + public void sendRemoveTeam(String teamName) { sendToNet(MSG_SEND_REMOVE_TEAM, teamName); } + public void sendTeamColorIndex(String teamName, int colorIndex) { sendToNet(MSG_SEND_TEAM_COLOR_INDEX, colorIndex, teamName); } + public void sendTeamHogCount(String teamName, int hogCount) { sendToNet(MSG_SEND_TEAM_HOG_COUNT, hogCount, teamName); } + public void sendEngineMessage(byte[] engineMessage) { sendToNet(MSG_SEND_ENGINE_MESSAGE, engineMessage); } + public void sendRoundFinished(boolean withoutError) { sendToNet(MSG_SEND_ROUND_FINISHED, Boolean.valueOf(withoutError)); } + public void sendToggleReady() { sendToNet(MSG_SEND_TOGGLE_READY); } - public void disconnect() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_DISCONNECT, "User Quit"); } + public void disconnect() { sendToNet(MSG_DISCONNECT, "User Quit"); } private static Netplay instance; @@ -179,51 +221,29 @@ } public boolean isChief() { - return chief; + if(netRoomState != null) { + return netRoomState.chief; + } else { + return false; + } } public String getPlayerName() { return playerName; } - /** - * Indicate that you want network messages to be checked regularly (several times per second). - * As long as nobody requests fast ticks, the network is only checked once every few seconds - * to conserve battery power. - * Once you no longer need fast updates, call unrequestFastTicks. - */ - public void requestFastTicks() { - if(foregroundUsers == Integer.MAX_VALUE) { - throw new RuntimeException("Reference counter overflow"); - } - if(foregroundUsers == 0 && connection != null) { - connection.setFastTickRate(true); - } - foregroundUsers++; - } - - public void unrequestFastTicks() { - if(foregroundUsers == 0) { - throw new RuntimeException("Reference counter underflow"); - } - foregroundUsers--; - if(foregroundUsers == 0 && connection != null) { - connection.setFastTickRate(false); - } - } - - private boolean sendToNet(int what) { + boolean sendToNet(ToNetMsgType what) { return sendToNet(what, 0, null); } - private boolean sendToNet(int what, Object obj) { + boolean sendToNet(ToNetMsgType what, Object obj) { return sendToNet(what, 0, obj); } - private boolean sendToNet(int what, int arg1, Object obj) { + boolean sendToNet(ToNetMsgType what, int arg1, Object obj) { if(connection != null) { Handler handler = connection.toNetHandler; - return handler.sendMessage(handler.obtainMessage(what, arg1, 0, obj)); + return handler.sendMessage(handler.obtainMessage(what.ordinal(), arg1, 0, obj)); } else { return false; } @@ -237,33 +257,44 @@ } } + public static enum FromNetMsgType { + MSG_LOBBY_JOIN, + MSG_LOBBY_LEAVE, + MSG_ROOM_JOIN, + MSG_ROOM_LEAVE, + MSG_CHAT, + MSG_MESSAGE, + MSG_ROOM_ADD, + MSG_ROOM_UPDATE, + MSG_ROOM_DELETE, + MSG_ROOMLIST, + MSG_CONNECTED, + MSG_DISCONNECTED, + MSG_PASSWORD_REQUEST, + MSG_ENTER_ROOM_FROM_LOBBY, + MSG_LEAVE_ROOM, + MSG_READYSTATE, + MSG_TEAM_ADDED, + MSG_TEAM_DELETED, + MSG_TEAM_ACCEPTED, + MSG_TEAM_COLOR_CHANGED, + MSG_HOG_COUNT_CHANGED, + MSG_ENGINE_MESSAGE, + MSG_RUN_GAME, + MSG_SCHEME_CHANGED, + MSG_MAP_CHANGED, + MSG_ROOM_CHIEF_STATUS_CHANGED, + MSG_SCRIPT_CHANGED, + MSG_WEAPONSET_CHANGED; + + static final List<FromNetMsgType> values = Collections.unmodifiableList(Arrays.asList(FromNetMsgType.values())); + } + /** * Processes messages from the networking system. Always runs on the main thread. */ @SuppressLint("HandlerLeak") final class FromNetHandler extends Handler { - public static final int MSG_LOBBY_JOIN = 0; - public static final int MSG_LOBBY_LEAVE = 1; - public static final int MSG_ROOM_JOIN = 2; - public static final int MSG_ROOM_LEAVE = 3; - public static final int MSG_CHAT = 4; - public static final int MSG_MESSAGE = 5; - public static final int MSG_ROOM_ADD = 6; - public static final int MSG_ROOM_UPDATE = 7; - public static final int MSG_ROOM_DELETE = 8; - public static final int MSG_ROOMLIST = 9; - public static final int MSG_CONNECTED = 10; - public static final int MSG_DISCONNECTED = 11; - public static final int MSG_PASSWORD_REQUEST = 12; - public static final int MSG_ENTER_ROOM_FROM_LOBBY = 13; - public static final int MSG_LEAVE_ROOM = 14; - public static final int MSG_READYSTATE = 15; - public static final int MSG_TEAM_ADDED = 16; - public static final int MSG_TEAM_DELETED = 17; - public static final int MSG_TEAM_ACCEPTED = 18; - public static final int MSG_TEAM_COLOR_CHANGED = 19; - public static final int MSG_HOG_COUNT_CHANGED = 20; - public FromNetHandler() { super(Looper.getMainLooper()); } @@ -271,10 +302,10 @@ @SuppressWarnings("unchecked") @Override public void handleMessage(Message msg) { - switch(msg.what) { + switch(FromNetMsgType.values.get(msg.what)) { case MSG_LOBBY_JOIN: { String name = (String)msg.obj; - lobbyPlayerlist.addPlayerWithNewId(name); + lobbyPlayerlist.put(name, new Player(name, false, false)); lobbyChatlog.appendPlayerJoin(name); break; } @@ -286,7 +317,12 @@ } case MSG_ROOM_JOIN: { String name = (String)msg.obj; - roomPlayerlist.addPlayerWithNewId(name); + Player p = lobbyPlayerlist.get(name); + if(p==null) { + Log.w("Netplay", "Unknown player joined room: "+name); + p = new Player(name, false, false); + } + roomPlayerlist.put(name, new PlayerInRoom(p, false)); roomChatlog.appendPlayerJoin(name); break; } @@ -299,18 +335,25 @@ case MSG_CHAT: { Pair<String, String> args = (Pair<String, String>)msg.obj; getCurrentLog().appendChat(args.first, args.second); + for(GameMessageListener listener : gameMessageListeners) { + listener.onChatMessage(args.first, args.second); + } break; } case MSG_MESSAGE: { getCurrentLog().appendMessage(msg.arg1, (String)msg.obj); + for(GameMessageListener listener : gameMessageListeners) { + listener.onMessage(1, (String)msg.obj); + } break; } case MSG_ROOM_ADD: { - roomList.addRoomWithNewId((RoomlistRoom)msg.obj); + Room room = (Room)msg.obj; + roomList.addRoomWithNewId(room); break; } case MSG_ROOM_UPDATE: { - Pair<String, RoomlistRoom> args = (Pair<String, RoomlistRoom>)msg.obj; + Pair<String, Room> args = (Pair<String, Room>)msg.obj; roomList.updateRoom(args.first, args.second); break; } @@ -319,7 +362,8 @@ break; } case MSG_ROOMLIST: { - roomList.updateList((RoomlistRoom[])msg.obj); + Room[] rooms = (Room[])msg.obj; + roomList.updateList(rooms); break; } case MSG_CONNECTED: { @@ -330,6 +374,9 @@ } case MSG_DISCONNECTED: { Pair<Boolean, String> args = (Pair<Boolean, String>)msg.obj; + for(GameMessageListener listener : gameMessageListeners) { + listener.onNetDisconnected(); + } changeState(State.NOT_CONNECTED); connection = null; Intent intent = new Intent(ACTION_DISCONNECTED); @@ -345,12 +392,8 @@ break; } case MSG_ENTER_ROOM_FROM_LOBBY: { - roomChatlog.clear(); - roomPlayerlist.clear(); - roomTeamlist.clear(); - roomRequestedTeams.clear(); + initRoomState((Boolean)msg.obj); changeState(State.ROOM); - chief = (Boolean)msg.obj; Intent intent = new Intent(ACTION_ENTERED_ROOM_FROM_LOBBY); broadcastManager.sendBroadcastSync(intent); break; @@ -365,15 +408,23 @@ } case MSG_READYSTATE: { Pair<String, Boolean> args = (Pair<String, Boolean>)msg.obj; - roomPlayerlist.setReady(args.first, args.second); + String name = args.first; + Boolean newReadyState = args.second; + PlayerInRoom oldEntry = roomPlayerlist.get(name); + if(oldEntry==null) { + Log.e("Netplay", "Setting readystate for unknown player "+name); + } else { + roomPlayerlist.put(name, new PlayerInRoom(oldEntry.player, newReadyState)); + } break; } case MSG_TEAM_ADDED: { Team newTeam = (Team)msg.obj; - TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, roomTeamlist.getUnusedOrRandomColorIndex(), TeamIngameAttributes.DEFAULT_HOG_COUNT, false); + int colorIndex = TeamInGame.getUnusedOrRandomColorIndex(roomTeamlist.getMap().values()); + TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, colorIndex, TeamIngameAttributes.DEFAULT_HOG_COUNT, true); TeamInGame tig = new TeamInGame(newTeam, attrs); - roomTeamlist.addTeamWithNewId(tig); - if(chief) { + roomTeamlist.put(newTeam.name, tig); + if(isChief()) { sendTeamColorIndex(newTeam.name, attrs.colorIndex); sendTeamHogCount(newTeam.name, attrs.hogCount); } @@ -386,10 +437,11 @@ case MSG_TEAM_ACCEPTED: { Team requestedTeam = roomRequestedTeams.remove(msg.obj); if(requestedTeam!=null) { - TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, roomTeamlist.getUnusedOrRandomColorIndex(), TeamIngameAttributes.DEFAULT_HOG_COUNT, false); + int colorIndex = TeamInGame.getUnusedOrRandomColorIndex(roomTeamlist.getMap().values()); + TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, colorIndex, TeamIngameAttributes.DEFAULT_HOG_COUNT, false); TeamInGame tig = new TeamInGame(requestedTeam, attrs); - roomTeamlist.addTeamWithNewId(tig); - if(chief) { + roomTeamlist.put(requestedTeam.name, tig); + if(isChief()) { sendTeamColorIndex(requestedTeam.name, attrs.colorIndex); sendTeamHogCount(requestedTeam.name, attrs.hogCount); } @@ -399,27 +451,59 @@ break; } case MSG_TEAM_COLOR_CHANGED: { - Pair<TeamInGame, Long> oldEntry = roomTeamlist.get((String)msg.obj); + TeamInGame oldEntry = roomTeamlist.get((String)msg.obj); if(oldEntry != null) { - TeamInGame tig = oldEntry.first; - TeamIngameAttributes tiga = tig.ingameAttribs.withColorIndex(msg.arg1); - roomTeamlist.put(tig.team.name, Pair.create(tig.withAttribs(tiga), oldEntry.second)); + TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withColorIndex(msg.arg1); + roomTeamlist.put(oldEntry.team.name, oldEntry.withAttribs(newAttribs)); } else { Log.e("Netplay", "Color update for unknown team "+msg.obj); } break; } case MSG_HOG_COUNT_CHANGED: { - Pair<TeamInGame, Long> oldEntry = roomTeamlist.get((String)msg.obj); + TeamInGame oldEntry = roomTeamlist.get((String)msg.obj); if(oldEntry != null) { - TeamInGame tig = oldEntry.first; - TeamIngameAttributes tiga = tig.ingameAttribs.withHogCount(msg.arg1); - roomTeamlist.put(tig.team.name, Pair.create(tig.withAttribs(tiga), oldEntry.second)); + TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withHogCount(msg.arg1); + roomTeamlist.put(oldEntry.team.name, oldEntry.withAttribs(newAttribs)); } else { Log.e("Netplay", "Hog count update for unknown team "+msg.obj); } break; } + case MSG_ENGINE_MESSAGE: { + byte[] em = (byte[])msg.obj; + for(GameMessageListener listener : gameMessageListeners) { + listener.onEngineMessage(em); + } + break; + } + case MSG_RUN_GAME: { + GameConfig config = (GameConfig)msg.obj; + for(RunGameListener listener : runGameListeners) { + listener.runGame(config); + } + break; + } + case MSG_MAP_CHANGED: { + netRoomState.setMapRecipe((MapRecipe)msg.obj); + break; + } + case MSG_ROOM_CHIEF_STATUS_CHANGED: { + netRoomState.setChief((Boolean)msg.obj); + break; + } + case MSG_SCHEME_CHANGED: { + netRoomState.setScheme((Scheme)msg.obj); + break; + } + case MSG_SCRIPT_CHANGED: { + netRoomState.setGameStyle((String)msg.obj); + break; + } + case MSG_WEAPONSET_CHANGED: { + netRoomState.setWeaponset((Weaponset)msg.obj); + break; + } default: { Log.e("FromNetHandler", "Unknown message type: "+msg.what); break; @@ -427,376 +511,4 @@ } } } - - /** - * This class handles the actual communication with the networking library, on a separate thread. - */ - private static class ThreadedNetConnection { - private static final long TICK_INTERVAL_FAST = 100; - private static final long TICK_INTERVAL_SLOW = 5000; - private static final Frontlib FLIB = Flib.INSTANCE; - - public final ToNetHandler toNetHandler; - - private final Context appContext; - private final FromNetHandler fromNetHandler; - private final TickHandler tickHandler; - - /** - * conn can only be null while connecting (the first thing in the thread), and directly after disconnecting, - * in the same message (the looper is shut down on disconnect, so there will be no messages after that). - */ - private NetconnPtr conn; - private String playerName; - - private ThreadedNetConnection(Context appContext, FromNetHandler fromNetHandler) { - this.appContext = appContext; - this.fromNetHandler = fromNetHandler; - - HandlerThread thread = new HandlerThread("NetThread"); - thread.start(); - toNetHandler = new ToNetHandler(thread.getLooper()); - tickHandler = new TickHandler(thread.getLooper(), TICK_INTERVAL_FAST, tickCb); - } - - private void connect(final String name, final String host, final int port) { - toNetHandler.post(new Runnable() { - public void run() { - playerName = name == null ? "Player" : name; - MetaschemePtr meta = null; - File dataPath; - try { - dataPath = Utils.getDataPathFile(appContext); - } catch (FileNotFoundException e) { - shutdown(true, appContext.getString(R.string.sdcard_not_mounted)); - return; - } - String metaschemePath = new File(dataPath, "metasettings.ini").getAbsolutePath(); - meta = FLIB.flib_metascheme_from_ini(metaschemePath); - if(meta == null) { - shutdown(true, appContext.getString(R.string.error_unexpected, "Missing metasettings.ini")); - return; - } - conn = FLIB.flib_netconn_create(playerName, meta, dataPath.getAbsolutePath(), host, port); - if(conn == null) { - shutdown(true, appContext.getString(R.string.error_connection_failed)); - return; - } - FLIB.flib_netconn_onLobbyJoin(conn, lobbyJoinCb, null); - FLIB.flib_netconn_onLobbyLeave(conn, lobbyLeaveCb, null); - FLIB.flib_netconn_onRoomJoin(conn, roomJoinCb, null); - FLIB.flib_netconn_onRoomLeave(conn, roomLeaveCb, null); - FLIB.flib_netconn_onChat(conn, chatCb, null); - FLIB.flib_netconn_onMessage(conn, messageCb, null); - FLIB.flib_netconn_onRoomAdd(conn, roomAddCb, null); - FLIB.flib_netconn_onRoomUpdate(conn, roomUpdateCb, null); - FLIB.flib_netconn_onRoomDelete(conn, roomDeleteCb, null); - FLIB.flib_netconn_onConnected(conn, connectedCb, null); - FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null); - FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null); - FLIB.flib_netconn_onPasswordRequest(conn, passwordRequestCb, null); - FLIB.flib_netconn_onEnterRoom(conn, enterRoomCb, null); - FLIB.flib_netconn_onLeaveRoom(conn, leaveRoomCb, null); - FLIB.flib_netconn_onReadyState(conn, readyStateCb, null); - FLIB.flib_netconn_onTeamAdd(conn, teamAddedCb, null); - FLIB.flib_netconn_onTeamDelete(conn, teamDeletedCb, null); - FLIB.flib_netconn_onTeamAccepted(conn, teamAcceptedCb, null); - FLIB.flib_netconn_onTeamColorChanged(conn, teamColorChangedCb, null); - FLIB.flib_netconn_onHogCountChanged(conn, hogCountChangedCb, null); - - FLIB.flib_metascheme_release(meta); - tickHandler.start(); - } - }); - } - - public static ThreadedNetConnection startConnection(Context appContext, FromNetHandler fromNetHandler, String playerName, String host, int port) { - ThreadedNetConnection result = new ThreadedNetConnection(appContext, fromNetHandler); - result.connect(playerName, host, port); - return result; - } - - public void setFastTickRate(boolean fastTickRate) { - tickHandler.setInterval(fastTickRate ? TICK_INTERVAL_FAST : TICK_INTERVAL_SLOW); - } - - private final Runnable tickCb = new Runnable() { - public void run() { - FLIB.flib_netconn_tick(conn); - } - }; - - private final StrCallback lobbyJoinCb = new StrCallback() { - public void callback(Pointer context, String name) { - sendFromNet(FromNetHandler.MSG_LOBBY_JOIN, name); - } - }; - - private final StrStrCallback lobbyLeaveCb = new StrStrCallback() { - public void callback(Pointer context, String name, String msg) { - sendFromNet(FromNetHandler.MSG_LOBBY_LEAVE, Pair.create(name, msg)); - } - }; - - private final StrCallback roomJoinCb = new StrCallback() { - public void callback(Pointer context, String name) { - sendFromNet(FromNetHandler.MSG_ROOM_JOIN, name); - } - }; - private final StrStrCallback roomLeaveCb = new StrStrCallback() { - public void callback(Pointer context, String name, String message) { - sendFromNet(FromNetHandler.MSG_ROOM_LEAVE, Pair.create(name, message)); - } - }; - private final StrStrCallback chatCb = new StrStrCallback() { - public void callback(Pointer context, String name, String msg) { - sendFromNet(FromNetHandler.MSG_CHAT, Pair.create(name, msg)); - } - }; - - private final IntStrCallback messageCb = new IntStrCallback() { - public void callback(Pointer context, int type, String msg) { - sendFromNet(FromNetHandler.MSG_MESSAGE, type, msg); - } - }; - - private final RoomCallback roomAddCb = new RoomCallback() { - public void callback(Pointer context, RoomPtr roomPtr) { - sendFromNet(FromNetHandler.MSG_ROOM_ADD, roomPtr.deref()); - } - }; - - private final StrRoomCallback roomUpdateCb = new StrRoomCallback() { - public void callback(Pointer context, String name, RoomPtr roomPtr) { - sendFromNet(FromNetHandler.MSG_ROOM_UPDATE, Pair.create(name, roomPtr.deref())); - } - }; - - private final StrCallback roomDeleteCb = new StrCallback() { - public void callback(Pointer context, final String name) { - sendFromNet(FromNetHandler.MSG_ROOM_DELETE, name); - } - }; - - private final RoomListCallback roomlistCb = new RoomListCallback() { - public void callback(Pointer context, RoomArrayPtr arg1, int count) { - sendFromNet(FromNetHandler.MSG_ROOMLIST, arg1.getRooms(count)); - } - }; - - private final VoidCallback connectedCb = new VoidCallback() { - public void callback(Pointer context) { - FLIB.flib_netconn_send_request_roomlist(conn); - playerName = FLIB.flib_netconn_get_playername(conn); - sendFromNet(FromNetHandler.MSG_CONNECTED, playerName); - } - }; - - private final StrCallback passwordRequestCb = new StrCallback() { - public void callback(Pointer context, String nickname) { - sendFromNet(FromNetHandler.MSG_PASSWORD_REQUEST, playerName); - } - }; - - private final BoolCallback enterRoomCb = new BoolCallback() { - public void callback(Pointer context, boolean isChief) { - sendFromNet(FromNetHandler.MSG_ENTER_ROOM_FROM_LOBBY, isChief); - } - }; - - private final IntStrCallback leaveRoomCb = new IntStrCallback() { - public void callback(Pointer context, int reason, String message) { - sendFromNet(FromNetHandler.MSG_LEAVE_ROOM, reason, message); - } - }; - - private final StrBoolCallback readyStateCb = new StrBoolCallback() { - public void callback(Pointer context, String player, boolean ready) { - sendFromNet(FromNetHandler.MSG_READYSTATE, Pair.create(player, ready)); - } - }; - - private final TeamCallback teamAddedCb = new TeamCallback() { - public void callback(Pointer context, TeamPtr team) { - sendFromNet(FromNetHandler.MSG_TEAM_ADDED, team.deref().team); - } - }; - - private final StrCallback teamDeletedCb = new StrCallback() { - public void callback(Pointer context, String teamName) { - sendFromNet(FromNetHandler.MSG_TEAM_DELETED, teamName); - } - }; - - private final StrCallback teamAcceptedCb = new StrCallback() { - public void callback(Pointer context, String teamName) { - sendFromNet(FromNetHandler.MSG_TEAM_ACCEPTED, teamName); - } - }; - - private final StrIntCallback teamColorChangedCb = new StrIntCallback() { - public void callback(Pointer context, String teamName, int colorIndex) { - sendFromNet(FromNetHandler.MSG_TEAM_COLOR_CHANGED, colorIndex, teamName); - } - }; - - private final StrIntCallback hogCountChangedCb = new StrIntCallback() { - public void callback(Pointer context, String teamName, int hogCount) { - sendFromNet(FromNetHandler.MSG_HOG_COUNT_CHANGED, hogCount, teamName); - } - }; - - private void shutdown(boolean error, String message) { - if(conn != null) { - FLIB.flib_netconn_destroy(conn); - conn = null; - } - tickHandler.stop(); - toNetHandler.getLooper().quit(); - sendFromNet(FromNetHandler.MSG_DISCONNECTED, Pair.create(error, message)); - } - - private final IntStrCallback disconnectCb = new IntStrCallback() { - public void callback(Pointer context, int reason, String message) { - Boolean error = reason != Frontlib.NETCONN_DISCONNECT_NORMAL; - String messageForUser = createDisconnectUserMessage(appContext.getResources(), reason, message); - shutdown(error, messageForUser); - } - }; - - private static String createDisconnectUserMessage(Resources res, int reason, String message) { - switch(reason) { - case Frontlib.NETCONN_DISCONNECT_AUTH_FAILED: - return res.getString(R.string.error_auth_failed); - case Frontlib.NETCONN_DISCONNECT_CONNLOST: - return res.getString(R.string.error_connection_lost); - case Frontlib.NETCONN_DISCONNECT_INTERNAL_ERROR: - return res.getString(R.string.error_unexpected, message); - case Frontlib.NETCONN_DISCONNECT_SERVER_TOO_OLD: - return res.getString(R.string.error_server_too_old); - default: - return message; - } - } - - private boolean sendFromNet(int what, Object obj) { - return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what, obj)); - } - - private boolean sendFromNet(int what, int arg1, Object obj) { - return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what, arg1, 0, obj)); - } - - /** - * Processes messages to the networking system. Runs on a non-main thread. - */ - @SuppressLint("HandlerLeak") - public final class ToNetHandler extends Handler { - public static final int MSG_SEND_NICK = 0; - public static final int MSG_SEND_PASSWORD = 1; - public static final int MSG_SEND_QUIT = 2; - public static final int MSG_SEND_ROOMLIST_REQUEST = 3; - public static final int MSG_SEND_PLAYER_INFO_REQUEST = 4; - public static final int MSG_SEND_CHAT = 5; - public static final int MSG_SEND_FOLLOW_PLAYER = 6; - public static final int MSG_SEND_JOIN_ROOM = 7; - public static final int MSG_SEND_CREATE_ROOM = 8; - public static final int MSG_SEND_LEAVE_ROOM = 9; - public static final int MSG_SEND_KICK = 10; - public static final int MSG_SEND_ADD_TEAM = 11; - public static final int MSG_SEND_REMOVE_TEAM = 12; - public static final int MSG_DISCONNECT = 13; - public static final int MSG_SEND_TEAM_COLOR_INDEX = 14; - public static final int MSG_SEND_TEAM_HOG_COUNT = 15; - - public ToNetHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch(msg.what) { - case MSG_SEND_NICK: { - FLIB.flib_netconn_send_nick(conn, (String)msg.obj); - break; - } - case MSG_SEND_PASSWORD: { - FLIB.flib_netconn_send_password(conn, (String)msg.obj); - break; - } - case MSG_SEND_QUIT: { - FLIB.flib_netconn_send_quit(conn, (String)msg.obj); - break; - } - case MSG_SEND_ROOMLIST_REQUEST: { - FLIB.flib_netconn_send_request_roomlist(conn); - break; - } - case MSG_SEND_PLAYER_INFO_REQUEST: { - FLIB.flib_netconn_send_playerInfo(conn, (String)msg.obj); - break; - } - case MSG_SEND_CHAT: { - if(FLIB.flib_netconn_send_chat(conn, (String)msg.obj) == 0) { - sendFromNet(FromNetHandler.MSG_CHAT, Pair.create(playerName, (String)msg.obj)); - } - break; - } - case MSG_SEND_FOLLOW_PLAYER: { - FLIB.flib_netconn_send_playerFollow(conn, (String)msg.obj); - break; - } - case MSG_SEND_JOIN_ROOM: { - FLIB.flib_netconn_send_joinRoom(conn, (String)msg.obj); - break; - } - case MSG_SEND_CREATE_ROOM: { - FLIB.flib_netconn_send_createRoom(conn, (String)msg.obj); - break; - } - case MSG_SEND_LEAVE_ROOM: { - if(FLIB.flib_netconn_send_leaveRoom(conn, (String)msg.obj) == 0) { - sendFromNet(FromNetHandler.MSG_LEAVE_ROOM, -1, ""); - } - break; - } - case MSG_SEND_KICK: { - FLIB.flib_netconn_send_kick(conn, (String)msg.obj); - break; - } - case MSG_SEND_ADD_TEAM: { - FLIB.flib_netconn_send_addTeam(conn, TeamPtr.createJavaOwned((Team)msg.obj)); - break; - } - case MSG_SEND_REMOVE_TEAM: { - if(FLIB.flib_netconn_send_removeTeam(conn, (String)msg.obj)==0) { - sendFromNet(FromNetHandler.MSG_TEAM_DELETED, msg.obj); - } - break; - } - case MSG_DISCONNECT: { - FLIB.flib_netconn_send_quit(conn, (String)msg.obj); - shutdown(false, "User quit"); - break; - } - case MSG_SEND_TEAM_COLOR_INDEX: { - if(FLIB.flib_netconn_send_teamColor(conn, (String)msg.obj, msg.arg1)==0) { - sendFromNet(FromNetHandler.MSG_TEAM_COLOR_CHANGED, msg.arg1, msg.obj); - } - break; - } - case MSG_SEND_TEAM_HOG_COUNT: { - if(FLIB.flib_netconn_send_teamHogCount(conn, (String)msg.obj, msg.arg1)==0) { - sendFromNet(FromNetHandler.MSG_HOG_COUNT_CHANGED, msg.arg1, msg.obj); - } - break; - } - default: { - Log.e("ToNetHandler", "Unknown message type: "+msg.what); - break; - } - } - } - } - } } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/NetplayStateFragment.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/NetplayStateFragment.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,121 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.frontlib.Frontlib; -import org.hedgewars.hedgeroid.netplay.Netplay.State; - -import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.content.LocalBroadcastManager; -import android.widget.Toast; - -/** - * Fragment for use by an activity that depends on the state of the network - * connection. The activity must implement the NetplayStateListener interface. - * - * This fragment manages a few aspects of the netplay connection: Requesting - * the network system loop to run at high frequency while the activity is in - * the foreground, and reacting to changes in the networking state by calling - * a callback method on the activity. - */ -public class NetplayStateFragment extends Fragment { - private Netplay netplay; - private Context appContext; - private LocalBroadcastManager broadcastManager; - private NetplayStateListener listener; - private State knownState; - - interface NetplayStateListener { - /** - * This is called while the activity is running, and every time during resume, if - * a change in the networking state is detected. It is also called once - * with the initial state (which could be called a change from the "unknown" state). - */ - void onNetplayStateChanged(State newState); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - try { - listener = (NetplayStateListener) activity; - } catch(ClassCastException e) { - throw new ClassCastException("Activity " + activity + " must implement NetplayStateListener to use NetplayStateFragment."); - } - } - - @Override - public void onDetach() { - super.onDetach(); - listener = null; - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - appContext = getActivity().getApplicationContext(); - broadcastManager = LocalBroadcastManager.getInstance(appContext); - netplay = Netplay.getAppInstance(appContext); - } - - @Override - public void onResume() { - super.onResume(); - broadcastManager.registerReceiver(disconnectReceiver, new IntentFilter(Netplay.ACTION_DISCONNECTED)); - broadcastManager.registerReceiver(leaveRoomReceiver, new IntentFilter(Netplay.ACTION_LEFT_ROOM)); - broadcastManager.registerReceiver(stateChangeReceiver, new IntentFilter(Netplay.ACTION_STATE_CHANGED)); - netplay.requestFastTicks(); - - State newState = netplay.getState(); - if(knownState != newState) { - listener.onNetplayStateChanged(newState); - knownState = newState; - } - } - - @Override - public void onPause() { - super.onPause(); - broadcastManager.unregisterReceiver(disconnectReceiver); - broadcastManager.unregisterReceiver(leaveRoomReceiver); - broadcastManager.unregisterReceiver(stateChangeReceiver); - netplay.unrequestFastTicks(); - } - - private final BroadcastReceiver disconnectReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if(intent.getBooleanExtra(Netplay.EXTRA_HAS_ERROR, true)) { - String message = intent.getStringExtra(Netplay.EXTRA_MESSAGE); - String toastText = getString(R.string.toast_disconnected, message); - Toast.makeText(appContext, toastText, Toast.LENGTH_LONG).show(); - } - } - }; - - private final BroadcastReceiver leaveRoomReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - int reason = intent.getIntExtra(Netplay.EXTRA_REASON, -1); - if(reason == Frontlib.NETCONN_ROOMLEAVE_ABANDONED) { - Toast.makeText(appContext, R.string.toast_room_abandoned, Toast.LENGTH_LONG).show(); - } else if(reason == Frontlib.NETCONN_ROOMLEAVE_KICKED) { - Toast.makeText(appContext, R.string.toast_kicked, Toast.LENGTH_LONG).show(); - } - } - }; - - private final BroadcastReceiver stateChangeReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - State newState = netplay.getState(); - listener.onNetplayStateChanged(newState); - knownState = newState; - } - }; -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ObservableTreeMap.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ObservableTreeMap.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; - -import android.database.DataSetObservable; - -public class ObservableTreeMap<K,V> extends DataSetObservable { - private final Map<K, V> map = new TreeMap<K, V>(); - - public void replaceContent(Map<? extends K, ? extends V> newMap) { - map.clear(); - map.putAll(newMap); - notifyChanged(); - } - - public void put(K key, V value) { - map.put(key, value); - notifyChanged(); - } - - public V get(K key) { - return map.get(key); - } - - public void remove(K key) { - if(map.remove(key) != null) { - notifyChanged(); - } - } - - public void clear() { - if(!map.isEmpty()) { - map.clear(); - notifyChanged(); - } - } - - public Map<K, V> getMap() { - return Collections.unmodifiableMap(map); - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ObservableTreeMapAdapter.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ObservableTreeMapAdapter.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import android.database.DataSetObserver; -import android.widget.BaseAdapter; - -public abstract class ObservableTreeMapAdapter<K,V> extends BaseAdapter { - private boolean sourceChanged = true; - private List<V> entries = new ArrayList<V>(); - private ObservableTreeMap<K, V> source; - - private DataSetObserver observer = new DataSetObserver() { - @Override - public void onChanged() { - sourceChanged = true; - notifyDataSetChanged(); - } - - @Override - public void onInvalidated() { - invalidate(); - } - }; - - abstract protected Comparator<V> getEntryOrder(); - - protected List<V> getEntries() { - if(sourceChanged) { - entries.clear(); - entries.addAll(source.getMap().values()); - Collections.sort(entries, getEntryOrder()); - sourceChanged = false; - } - return entries; - } - - public int getCount() { - return getEntries().size(); - } - - public void setSource(ObservableTreeMap<K,V> source) { - if(this.source != null) { - this.source.unregisterObserver(observer); - } - this.source = source; - this.source.registerObserver(observer); - sourceChanged = true; - notifyDataSetChanged(); - } - - public void invalidate() { - if(source != null) { - source.unregisterObserver(observer); - } - source = null; - notifyDataSetInvalidated(); - } -} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/OpenConnectionService.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/OpenConnectionService.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import android.app.Service; -import android.content.Intent; -import android.os.IBinder; - -public class OpenConnectionService extends Service { - @Override - public IBinder onBind(Intent intent) { - return null; - } - -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomActivity.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.Datastructures.Team; -import org.hedgewars.hedgeroid.netplay.Netplay.State; -import org.hedgewars.hedgeroid.netplay.NetplayStateFragment.NetplayStateListener; - -import android.os.Bundle; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentTransaction; -import android.widget.TabHost; -import android.widget.Toast; - -public class RoomActivity extends FragmentActivity implements NetplayStateListener, TeamAddDialog.Listener { - private TabHost tabHost; - private Netplay netplay; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - netplay = Netplay.getAppInstance(getApplicationContext()); - - setContentView(R.layout.activity_netroom); - ChatFragment chatFragment = (ChatFragment)getSupportFragmentManager().findFragmentById(R.id.chatFragment); - chatFragment.setInRoom(true); - - FragmentTransaction trans = getSupportFragmentManager().beginTransaction(); - trans.add(new NetplayStateFragment(), "netplayFragment"); - trans.commit(); - - /*tabHost = (TabHost)findViewById(android.R.id.tabhost); - if(tabHost != null) { - tabHost.setup(); - tabHost.getTabWidget().setOrientation(LinearLayout.VERTICAL); - - //tabHost.addTab(tabHost.newTabSpec("chat").setIndicator(createIndicatorView(tabHost, R.string.lobby_tab_chat, getResources().getDrawable(R.drawable.edit))).setContent(R.id.chatFragment)); - //tabHost.addTab(tabHost.newTabSpec("players").setIndicator(createIndicatorView(tabHost, R.string.lobby_tab_players, getResources().getDrawable(R.drawable.human))).setContent(R.id.playerListFragment)); - - if (icicle != null) { - tabHost.setCurrentTabByTag(icicle.getString("currentTab")); - } - }*/ - } - - @Override - public void onBackPressed() { - netplay.sendLeaveRoom(null); - } - - @Override - protected void onSaveInstanceState(Bundle icicle) { - super.onSaveInstanceState(icicle); - if(tabHost != null) { - icicle.putString("currentTab", tabHost.getCurrentTabTag()); - } - } - - public void onNetplayStateChanged(State newState) { - switch(newState) { - case NOT_CONNECTED: - case CONNECTING: - case LOBBY: - finish(); - break; - case ROOM: - // Do nothing - break; - case INGAME: - //startActivity(new Intent(getApplicationContext(), RoomActivity.class)); - Toast.makeText(getApplicationContext(), R.string.not_implemented_yet, Toast.LENGTH_SHORT).show(); - break; - default: - throw new IllegalStateException("Unknown connection state: "+newState); - } - } - - public void onTeamAddDialogSubmitted(Team newTeam) { - netplay.sendAddTeam(newTeam); - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomPlayerlist.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomPlayerlist.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import org.hedgewars.hedgeroid.Datastructures.Player; -import org.hedgewars.hedgeroid.netplay.RoomPlayerlist.PlayerInRoom; - -import android.util.Log; - -public class RoomPlayerlist extends ObservableTreeMap<String, PlayerInRoom> { - private long nextId = 1; - - public void addPlayerWithNewId(String name) { - put(name, new PlayerInRoom(new Player(name), nextId++, false)); - } - - public void setReady(String name, boolean ready) { - PlayerInRoom oldEntry = get(name); - if(oldEntry==null) { - Log.e("RoomPlayerlist", "Setting readystate for unknown player "+name); - } else { - put(name, new PlayerInRoom(oldEntry.player, oldEntry.id, ready)); - } - } - - // Immutable - public static class PlayerInRoom { - public final Player player; - public final long id; - public final boolean ready; - - public PlayerInRoom(Player player, long id, boolean ready) { - this.player = player; - this.id = id; - this.ready = ready; - } - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomPlayerlistAdapter.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomPlayerlistAdapter.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import java.util.Comparator; - -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.netplay.RoomPlayerlist.PlayerInRoom; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -public class RoomPlayerlistAdapter extends ObservableTreeMapAdapter<String, PlayerInRoom> { - private Context context; - - public RoomPlayerlistAdapter(Context context) { - this.context = context; - } - - @Override - protected Comparator<PlayerInRoom> getEntryOrder() { - return AlphabeticalOrderComparator.INSTANCE; - } - - public PlayerInRoom getItem(int position) { - return getEntries().get(position); - } - - public long getItemId(int position) { - return getEntries().get(position).id; - } - - public boolean hasStableIds() { - return true; - } - - public View getView(int position, View convertView, ViewGroup parent) { - View v = convertView; - if (v == null) { - LayoutInflater vi = LayoutInflater.from(context); - v = vi.inflate(R.layout.listview_player, null); - } - - PlayerInRoom player = getItem(position); - TextView username = (TextView) v.findViewById(android.R.id.text1); - username.setText(player.player.name); - int readyDrawable = player.ready ? R.drawable.lightbulb_on : R.drawable.lightbulb_off; - username.setCompoundDrawablesWithIntrinsicBounds(readyDrawable, 0, 0, 0); - return v; - } - - private static final class AlphabeticalOrderComparator implements Comparator<PlayerInRoom> { - public static final AlphabeticalOrderComparator INSTANCE = new AlphabeticalOrderComparator(); - public int compare(PlayerInRoom lhs, PlayerInRoom rhs) { - return lhs.player.name.compareToIgnoreCase(rhs.player.name); - }; - } -} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomPlayerlistFragment.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomPlayerlistFragment.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.netplay.RoomPlayerlist.PlayerInRoom; - -import android.os.Bundle; -import android.support.v4.app.ListFragment; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.LayoutInflater; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView.AdapterContextMenuInfo; - -public class RoomPlayerlistFragment extends ListFragment { - private Netplay netplay; - private RoomPlayerlistAdapter adapter; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - netplay = Netplay.getAppInstance(getActivity().getApplicationContext()); - adapter = new RoomPlayerlistAdapter(getActivity()); - adapter.setSource(netplay.roomPlayerlist); - setListAdapter(adapter); - } - - @Override - public void onDestroy() { - super.onDestroy(); - adapter.invalidate(); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - registerForContextMenu(getListView()); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo; - String playerName = adapter.getItem(info.position).player.name; - - MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.room_playerlist_context, menu); - if(netplay.isChief() && !playerName.equals(netplay.getPlayerName())) { - inflater.inflate(R.menu.room_playerlist_chief_context, menu); - } - menu.setHeaderIcon(R.drawable.human); - menu.setHeaderTitle(playerName); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo(); - PlayerInRoom player = adapter.getItem(info.position); - switch(item.getItemId()) { - case R.id.player_info: - netplay.sendPlayerInfoQuery(player.player.name); - return true; - case R.id.player_kick: - netplay.sendKick(player.player.name); - return true; - default: - return super.onContextItemSelected(item); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_playerlist, container, false); - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Roomlist.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Roomlist.java Sat Aug 18 00:22:33 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Roomlist.java Sat Aug 18 00:47:51 2012 +0200 @@ -3,37 +3,37 @@ import java.util.Map; import java.util.TreeMap; -import org.hedgewars.hedgeroid.Datastructures.RoomlistRoom; +import org.hedgewars.hedgeroid.Datastructures.Room; +import org.hedgewars.hedgeroid.Datastructures.RoomWithId; +import org.hedgewars.hedgeroid.util.ObservableTreeMap; -import android.util.Pair; - -public class Roomlist extends ObservableTreeMap<String, Pair<RoomlistRoom, Long>> { +public class Roomlist extends ObservableTreeMap<String, RoomWithId> { private long nextId = 1; - public void updateList(RoomlistRoom[] newRooms) { - Map<String, Pair<RoomlistRoom, Long>> newMap = new TreeMap<String, Pair<RoomlistRoom, Long>>(); - for(RoomlistRoom room : newRooms) { - Pair<RoomlistRoom, Long> oldEntry = get(room.name); + public void updateList(Room[] newRooms) { + Map<String, RoomWithId> newMap = new TreeMap<String, RoomWithId>(); + for(Room room : newRooms) { + RoomWithId oldEntry = get(room.name); if(oldEntry == null) { - newMap.put(room.name, Pair.create(room, nextId++)); + newMap.put(room.name, new RoomWithId(room, nextId++)); } else { - newMap.put(room.name, Pair.create(room, oldEntry.second)); + newMap.put(room.name, new RoomWithId(room, oldEntry.id)); } } replaceContent(newMap); } - public void addRoomWithNewId(RoomlistRoom room) { - put(room.name, Pair.create(room, nextId++)); + public void addRoomWithNewId(Room room) { + put(room.name, new RoomWithId(room, nextId++)); } - public void updateRoom(String name, RoomlistRoom room) { - Pair<RoomlistRoom, Long> oldEntry = get(name); + public void updateRoom(String name, Room room) { + RoomWithId oldEntry = get(name); if(oldEntry == null) { addRoomWithNewId(room); } else { remove(name); - put(room.name, Pair.create(room, oldEntry.second)); + put(room.name, new RoomWithId(room, oldEntry.id)); } } } diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomlistAdapter.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomlistAdapter.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import java.util.Comparator; - -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.Datastructures.RoomlistRoom; - -import android.content.Context; -import android.content.res.Resources; -import android.util.Pair; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -public class RoomlistAdapter extends ObservableTreeMapAdapter<String, Pair<RoomlistRoom, Long>> { - private Context context; - - public RoomlistAdapter(Context context) { - this.context = context; - } - - @Override - protected Comparator<Pair<RoomlistRoom, Long>> getEntryOrder() { - return RoomAgeComparator.INSTANCE; - } - - public RoomlistRoom getItem(int position) { - return getEntries().get(position).first; - } - - public long getItemId(int position) { - return getEntries().get(position).second; - } - - public boolean hasStableIds() { - return true; - } - - private static CharSequence formatExtra(Resources res, RoomlistRoom room) { - String ownermsg = res.getString(R.string.roomlist_owner, room.owner); - String mapmsg = res.getString(R.string.roomlist_map, room.formatMapName(res)); - String scheme = room.scheme.equals(room.weapons) ? room.scheme : room.scheme + " / " + room.weapons; - String schememsg = res.getString(R.string.roomlist_scheme, scheme); - return ownermsg + ". " + mapmsg + ", " + schememsg; - } - - public View getView(int position, View convertView, ViewGroup parent) { - View v = convertView; - if (v == null) { - LayoutInflater vi = LayoutInflater.from(context); - v = vi.inflate(R.layout.listview_room, null); - } - - RoomlistRoom room = getItem(position); - int iconRes = room.inProgress ? R.drawable.roomlist_ingame : R.drawable.roomlist_preparing; - - if(v.findViewById(android.R.id.text1) == null) { - // Tabular room list - TextView roomnameView = (TextView)v.findViewById(R.id.roomname); - TextView playerCountView = (TextView)v.findViewById(R.id.playercount); - TextView teamCountView = (TextView)v.findViewById(R.id.teamcount); - TextView ownerView = (TextView)v.findViewById(R.id.owner); - TextView mapView = (TextView)v.findViewById(R.id.map); - TextView schemeView = (TextView)v.findViewById(R.id.scheme); - TextView weaponView = (TextView)v.findViewById(R.id.weapons); - - roomnameView.setCompoundDrawablesWithIntrinsicBounds(iconRes, 0, 0, 0); - roomnameView.setText(room.name); - if(playerCountView != null) { - playerCountView.setText(String.valueOf(room.playerCount)); - } - if(teamCountView != null) { - teamCountView.setText(String.valueOf(room.teamCount)); - } - ownerView.setText(room.owner); - mapView.setText(room.formatMapName(context.getResources())); - schemeView.setText(room.scheme); - weaponView.setText(room.weapons); - } else { - // Small room list - TextView v1 = (TextView)v.findViewById(android.R.id.text1); - TextView v2 = (TextView)v.findViewById(android.R.id.text2); - - v1.setCompoundDrawablesWithIntrinsicBounds(iconRes, 0, 0, 0); - v1.setText(room.name); - v2.setText(formatExtra(context.getResources(), room)); - } - - return v; - } - - private static final class RoomAgeComparator implements Comparator<Pair<RoomlistRoom, Long>> { - public static final RoomAgeComparator INSTANCE = new RoomAgeComparator(); - public int compare(Pair<RoomlistRoom, Long> lhs, Pair<RoomlistRoom, Long> rhs) { - return rhs.second.compareTo(lhs.second); - } - } -} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomlistFragment.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomlistFragment.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import org.hedgewars.hedgeroid.R; - -import android.os.Bundle; -import android.os.CountDownTimer; -import android.support.v4.app.ListFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; - -public class RoomlistFragment extends ListFragment implements OnItemClickListener { - private static final int AUTO_REFRESH_INTERVAL_MS = 15000; - - private Netplay netplay; - private RoomlistAdapter adapter; - private CountDownTimer autoRefreshTimer = new CountDownTimer(Long.MAX_VALUE, AUTO_REFRESH_INTERVAL_MS) { - @Override - public void onTick(long millisUntilFinished) { - netplay.sendRoomlistRequest(); - } - - @Override - public void onFinish() { } - }; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - netplay = Netplay.getAppInstance(getActivity().getApplicationContext()); - adapter = new RoomlistAdapter(getActivity()); - adapter.setSource(netplay.roomList); - setListAdapter(adapter); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - return inflater.inflate(R.layout.lobby_rooms_fragment, container, false); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - getListView().setOnItemClickListener(this); - } - - @Override - public void onResume() { - super.onResume(); - autoRefreshTimer.start(); - } - - @Override - public void onPause() { - super.onPause(); - autoRefreshTimer.cancel(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - adapter.invalidate(); - } - - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - netplay.sendJoinRoom(adapter.getItem(position).name); - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RunGameListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RunGameListener.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,7 @@ +package org.hedgewars.hedgeroid.netplay; + +import org.hedgewars.hedgeroid.Datastructures.GameConfig; + +public interface RunGameListener { + void runGame(GameConfig config); +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamAddDialog.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamAddDialog.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils; -import org.hedgewars.hedgeroid.Datastructures.Team; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.os.Bundle; -import android.support.v4.app.DialogFragment; - -public class TeamAddDialog extends DialogFragment { - private static final String STATE_TEAMS_ALREADY_IN_GAME = "teamAlreadyInGame"; - private ArrayList<String> teamsAlreadyInGame; - private List<Team> availableTeams; - private Listener listener; - - public static interface Listener { - void onTeamAddDialogSubmitted(Team newTeam); - } - - public TeamAddDialog() { - // Only for reflection-based instantiation by the framework - } - - TeamAddDialog(Collection<String> teamsAlreadyInGame) { - this.teamsAlreadyInGame = new ArrayList<String>(teamsAlreadyInGame); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - try { - listener = (Listener) activity; - } catch(ClassCastException e) { - throw new ClassCastException("Activity " + activity + " must implement TeamAddDialog.Listener to use TeamAddDialog."); - } - } - - @Override - public void onDetach() { - super.onDetach(); - listener = null; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if(savedInstanceState != null) { - teamsAlreadyInGame = savedInstanceState.getStringArrayList(STATE_TEAMS_ALREADY_IN_GAME); - } - availableTeams = new ArrayList<Team>(); - List<Team> teams = FrontendDataUtils.getTeams(getActivity()); - for(Team team : teams) { - if(!teamsAlreadyInGame.contains(team.name)) { - availableTeams.add(team); - } - } - Collections.sort(availableTeams, Team.NAME_ORDER); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(R.string.dialog_addteam_title); - builder.setIcon(R.drawable.human); - String[] teamNames = new String[availableTeams.size()]; - for(int i=0; i<availableTeams.size(); i++) { - teamNames[i] = availableTeams.get(i).name; - } - builder.setItems(teamNames, new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - listener.onTeamAddDialogSubmitted(availableTeams.get(which)); - } - }); - return builder.create(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putStringArrayList(STATE_TEAMS_ALREADY_IN_GAME, teamsAlreadyInGame); - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Teamlist.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Teamlist.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import java.util.Collection; - -import org.hedgewars.hedgeroid.Datastructures.TeamInGame; -import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes; - -import android.util.Pair; - -public class Teamlist extends ObservableTreeMap<String, Pair<TeamInGame, Long>> { - private long nextId = 1; - - public void addTeamWithNewId(TeamInGame team) { - put(team.team.name, Pair.create(team, nextId++)); - } - - public int getUnusedOrRandomColorIndex() { - Collection<Pair<TeamInGame, Long>> teams = getMap().values(); - int[] illegalColors = new int[teams.size()]; - int i=0; - for(Pair<TeamInGame, Long> item : teams) { - illegalColors[i] = item.first.ingameAttribs.colorIndex; - i++; - } - return TeamIngameAttributes.randomColorIndex(illegalColors); - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamlistAdapter.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamlistAdapter.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import java.util.Comparator; - -import org.hedgewars.hedgeroid.Datastructures.TeamInGame; - -import android.content.Context; -import android.util.Pair; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -public class TeamlistAdapter extends ObservableTreeMapAdapter<String, Pair<TeamInGame, Long>> { - private Context context; - - public TeamlistAdapter(Context context) { - this.context = context; - } - - @Override - protected Comparator<Pair<TeamInGame, Long>> getEntryOrder() { - return AlphabeticalOrderComparator.INSTANCE; - } - - public TeamInGame getItem(int position) { - return getEntries().get(position).first; - } - - public long getItemId(int position) { - return getEntries().get(position).second; - } - - public boolean hasStableIds() { - return true; - } - - public View getView(int position, View convertView, ViewGroup parent) { - View v = convertView; - if (v == null) { - LayoutInflater vi = LayoutInflater.from(context); - v = vi.inflate(android.R.layout.simple_list_item_2, null); - } - - TeamInGame team = getItem(position); - TextView tv1 = (TextView) v.findViewById(android.R.id.text1); - TextView tv2 = (TextView) v.findViewById(android.R.id.text2); - - tv1.setText(team.team.name); - tv2.setText("Hogs: "+team.ingameAttribs.hogCount); - return v; - } - - private static final class AlphabeticalOrderComparator implements Comparator<Pair<TeamInGame, Long>> { - public static final AlphabeticalOrderComparator INSTANCE = new AlphabeticalOrderComparator(); - public int compare(Pair<TeamInGame, Long> lhs, Pair<TeamInGame, Long> rhs) { - return lhs.first.team.name.compareToIgnoreCase(rhs.first.team.name); - }; - } -} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamlistFragment.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamlistFragment.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.Datastructures.Team; -import org.hedgewars.hedgeroid.Datastructures.TeamInGame; - -import android.database.DataSetObserver; -import android.os.Bundle; -import android.support.v4.app.ListFragment; -import android.util.Pair; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.AdapterView; -import android.widget.Button; - -public class TeamlistFragment extends ListFragment implements OnItemClickListener { - private Netplay netplay; - private TeamlistAdapter adapter; - private Button addTeamButton; - private DataSetObserver teamlistObserver; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - netplay = Netplay.getAppInstance(getActivity().getApplicationContext()); - adapter = new TeamlistAdapter(getActivity()); - adapter.setSource(netplay.roomTeamlist); - setListAdapter(adapter); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_teamlist, container, false); - addTeamButton = (Button)v.findViewById(R.id.addTeamButton); - addTeamButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - new TeamAddDialog(getCurrentTeamNames()).show(getFragmentManager(), "team_add_dialog"); - } - }); - - teamlistObserver = new DataSetObserver() { - @Override - public void onChanged() { - addTeamButton.setEnabled(netplay.roomTeamlist.getMap().size() < Team.maxNumberOfTeams); - } - }; - netplay.roomTeamlist.registerObserver(teamlistObserver); - teamlistObserver.onChanged(); - - return v; - } - - @Override - public void onDestroy() { - super.onDestroy(); - adapter.invalidate(); - netplay.roomTeamlist.unregisterObserver(teamlistObserver); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - getListView().setOnItemClickListener(this); - } - - private Collection<String> getCurrentTeamNames() { - List<String> names = new ArrayList<String>(); - for(Pair<TeamInGame, Long> teamWithId : netplay.roomTeamlist.getMap().values()) { - names.add(teamWithId.first.team.name); - } - return names; - } - - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - netplay.sendRemoveTeam(adapter.getItem(position).team.name); - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TextInputDialog.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TextInputDialog.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,128 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -import android.view.KeyEvent; -import android.view.inputmethod.EditorInfo; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; - -/** - * A generic text input dialog with configurable text. The Activity must implement the callback - * interface TextInputDialogListener, which will be called by the dialog if it is submitted or cancelled. - */ -public class TextInputDialog extends DialogFragment { - private static final String BUNDLE_DIALOG_ID = "dialogId"; - private static final String BUNDLE_TITLE_TEXT = "title"; - private static final String BUNDLE_MESSAGE_TEXT = "message"; - private static final String BUNDLE_HINT_TEXT = "hint"; - - private int dialogId, titleText, messageText, hintText; - private TextInputDialogListener listener; - - public interface TextInputDialogListener { - void onTextInputDialogSubmitted(int dialogId, String text); - void onTextInputDialogCancelled(int dialogId); - } - - /** - * The dialogId is only used for passing back to the callback on the activity, the - * other parameters are text resource IDs. Pass 0 for any of them to not use this - * text. - */ - public TextInputDialog(int dialogId, int titleText, int messageText, int hintText) { - this.dialogId = dialogId; - this.titleText = titleText; - this.messageText = messageText; - this.hintText = hintText; - } - - public TextInputDialog() { - // Only for reflection-based instantiation by the framework - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - try { - listener = (TextInputDialogListener) activity; - } catch(ClassCastException e) { - throw new ClassCastException("Activity " + activity + " must implement TextInputDialogListener to use TextInputDialog."); - } - } - - @Override - public void onDetach() { - super.onDetach(); - listener = null; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - if(savedInstanceState != null) { - dialogId = savedInstanceState.getInt(BUNDLE_DIALOG_ID, dialogId); - titleText = savedInstanceState.getInt(BUNDLE_TITLE_TEXT, titleText); - messageText = savedInstanceState.getInt(BUNDLE_MESSAGE_TEXT, messageText); - hintText = savedInstanceState.getInt(BUNDLE_HINT_TEXT, hintText); - } - - final EditText editText = new EditText(getActivity()); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - - if(titleText != 0) { - builder.setTitle(titleText); - } - if(messageText != 0) { - builder.setTitle(messageText); - } - if(hintText != 0) { - editText.setHint(hintText); - } - - editText.setId(android.R.id.text1); - editText.setImeOptions(EditorInfo.IME_ACTION_DONE); - editText.setSingleLine(); - - builder.setView(editText); - builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.cancel(); - } - }); - - editText.setOnEditorActionListener(new OnEditorActionListener() { - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - listener.onTextInputDialogSubmitted(dialogId, v.getText().toString()); - return true; - } - }); - - builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - listener.onTextInputDialogSubmitted(dialogId, editText.getText().toString()); - } - }); - - return builder.create(); - } - - @Override - public void onSaveInstanceState(Bundle icicle) { - super.onSaveInstanceState(icicle); - icicle.putInt(BUNDLE_DIALOG_ID, dialogId); - icicle.putInt(BUNDLE_TITLE_TEXT, titleText); - icicle.putInt(BUNDLE_MESSAGE_TEXT, messageText); - icicle.putInt(BUNDLE_HINT_TEXT, hintText); - } - - @Override - public void onCancel(DialogInterface dialog) { - super.onCancel(dialog); - listener.onTextInputDialogCancelled(dialogId); - } -} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ThreadedNetConnection.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ThreadedNetConnection.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,566 @@ +package org.hedgewars.hedgeroid.netplay; + +import static org.hedgewars.hedgeroid.netplay.Netplay.FromNetMsgType.*; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; +import org.hedgewars.hedgeroid.Datastructures.Scheme; +import org.hedgewars.hedgeroid.Datastructures.Team; +import org.hedgewars.hedgeroid.Datastructures.Weaponset; +import org.hedgewars.hedgeroid.frontlib.Flib; +import org.hedgewars.hedgeroid.frontlib.Frontlib; +import org.hedgewars.hedgeroid.frontlib.Frontlib.BoolCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.BytesCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.GameSetupPtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.IntStrCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.MapIntCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.MapRecipePtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.NetconnPtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomArrayPtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomListCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomPtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.SchemeCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.SchemePtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.StrIntCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.StrRoomCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.StrStrCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.VoidCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.WeaponsetCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.WeaponsetPtr; +import org.hedgewars.hedgeroid.netplay.Netplay.FromNetHandler; +import org.hedgewars.hedgeroid.netplay.Netplay.FromNetMsgType; +import org.hedgewars.hedgeroid.util.FileUtils; +import org.hedgewars.hedgeroid.util.TickHandler; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.Resources; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.util.Pair; + +import com.sun.jna.Memory; +import com.sun.jna.NativeLong; +import com.sun.jna.Pointer; + +/** + * This class handles the actual communication with the networking library, running on a separate thread. + * + * In order to process net messages, this class regularly runs a tick() function on the frontlib. This + * usually happens several times per second, but it can be slowed down a lot if no fast reaction to + * events is required (e.g. to conserve battery if the application is in the background). + */ +class ThreadedNetConnection { + private static final long TICK_INTERVAL_FAST = 100; + private static final long TICK_INTERVAL_SLOW = 5000; + private static final Frontlib FLIB = Flib.INSTANCE; + + public final ToNetHandler toNetHandler; + + private final Context appContext; + private final FromNetHandler fromNetHandler; + private final TickHandler tickHandler; + + /** + * conn can only be null while connecting (the first thing in the thread), and directly after disconnecting, + * in the same message (the looper is shut down on disconnect, so there will be no messages after that). + */ + private NetconnPtr conn; + private String playerName; + + private ThreadedNetConnection(Context appContext, FromNetHandler fromNetHandler) { + this.appContext = appContext; + this.fromNetHandler = fromNetHandler; + + HandlerThread thread = new HandlerThread("NetThread"); + thread.start(); + toNetHandler = new ToNetHandler(thread.getLooper()); + tickHandler = new TickHandler(thread.getLooper(), TICK_INTERVAL_FAST, tickCb); + } + + private void connect(final String name, final String host, final int port) { + toNetHandler.post(new Runnable() { + public void run() { + playerName = name == null ? "Player" : name; + File dataPath; + try { + dataPath = FileUtils.getDataPathFile(appContext); + } catch (FileNotFoundException e) { + shutdown(true, appContext.getString(R.string.sdcard_not_mounted)); + return; + } + conn = FLIB.flib_netconn_create(playerName, dataPath.getAbsolutePath()+"/", host, port); + if(conn == null) { + shutdown(true, appContext.getString(R.string.error_connection_failed)); + return; + } + + //FLIB.flib_netconn_onAdminAccess(conn, adminAccessCb, null) + FLIB.flib_netconn_onCfgScheme(conn, cfgSchemeCb, null); + FLIB.flib_netconn_onChat(conn, chatCb, null); + FLIB.flib_netconn_onConnected(conn, connectedCb, null); + FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null); + FLIB.flib_netconn_onEngineMessage(conn, engineMessageCb, null); + FLIB.flib_netconn_onEnterRoom(conn, enterRoomCb, null); + FLIB.flib_netconn_onHogCountChanged(conn, hogCountChangedCb, null); + FLIB.flib_netconn_onLeaveRoom(conn, leaveRoomCb, null); + FLIB.flib_netconn_onLobbyJoin(conn, lobbyJoinCb, null); + FLIB.flib_netconn_onLobbyLeave(conn, lobbyLeaveCb, null); + FLIB.flib_netconn_onMapChanged(conn, mapChangedCb, null); + FLIB.flib_netconn_onMessage(conn, messageCb, null); + FLIB.flib_netconn_onPasswordRequest(conn, passwordRequestCb, null); + FLIB.flib_netconn_onReadyState(conn, readyStateCb, null); + FLIB.flib_netconn_onRoomAdd(conn, roomAddCb, null); + FLIB.flib_netconn_onRoomChiefStatus(conn, roomChiefStatusCb, null); + FLIB.flib_netconn_onRoomDelete(conn, roomDeleteCb, null); + FLIB.flib_netconn_onRoomJoin(conn, roomJoinCb, null); + FLIB.flib_netconn_onRoomLeave(conn, roomLeaveCb, null); + FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null); + FLIB.flib_netconn_onRoomUpdate(conn, roomUpdateCb, null); + FLIB.flib_netconn_onRunGame(conn, runGameCb, null); + FLIB.flib_netconn_onScriptChanged(conn, scriptChangedCb, null); + // FLIB.flib_netconn_onServerVar(conn, serverVarCb, null); + FLIB.flib_netconn_onTeamAccepted(conn, teamAcceptedCb, null); + FLIB.flib_netconn_onTeamAdd(conn, teamAddedCb, null); + FLIB.flib_netconn_onTeamColorChanged(conn, teamColorChangedCb, null); + FLIB.flib_netconn_onTeamDelete(conn, teamDeletedCb, null); + FLIB.flib_netconn_onWeaponsetChanged(conn, weaponsetChangedCb, null); + + tickHandler.start(); + } + }); + } + + public static ThreadedNetConnection startConnection(Context appContext, FromNetHandler fromNetHandler, String playerName, String host, int port) { + ThreadedNetConnection result = new ThreadedNetConnection(appContext, fromNetHandler); + result.connect(playerName, host, port); + return result; + } + + public void setFastTickRate(boolean fastTickRate) { + tickHandler.setInterval(fastTickRate ? TICK_INTERVAL_FAST : TICK_INTERVAL_SLOW); + } + + private final Runnable tickCb = new Runnable() { + public void run() { + FLIB.flib_netconn_tick(conn); + } + }; + + private final SchemeCallback cfgSchemeCb = new SchemeCallback() { + public void callback(Pointer context, SchemePtr schemePtr) { + sendFromNet(MSG_SCHEME_CHANGED, schemePtr.deref()); + } + }; + + private final MapIntCallback mapChangedCb = new MapIntCallback() { + public void callback(Pointer context, MapRecipePtr mapPtr, int updateType) { + sendFromNet(MSG_MAP_CHANGED, updateType, mapPtr.deref()); + } + }; + + private final BoolCallback roomChiefStatusCb = new BoolCallback() { + public void callback(Pointer context, boolean chief) { + sendFromNet(MSG_ROOM_CHIEF_STATUS_CHANGED, chief); + } + }; + + private final StrCallback scriptChangedCb = new StrCallback() { + public void callback(Pointer context, String script) { + sendFromNet(MSG_SCRIPT_CHANGED, script); + } + }; + + private final WeaponsetCallback weaponsetChangedCb = new WeaponsetCallback() { + public void callback(Pointer context, WeaponsetPtr weaponsetPtr) { + sendFromNet(MSG_WEAPONSET_CHANGED, weaponsetPtr.deref()); + } + }; + + private final StrCallback lobbyJoinCb = new StrCallback() { + public void callback(Pointer context, String name) { + sendFromNet(MSG_LOBBY_JOIN, name); + } + }; + + private final StrStrCallback lobbyLeaveCb = new StrStrCallback() { + public void callback(Pointer context, String name, String msg) { + sendFromNet(MSG_LOBBY_LEAVE, Pair.create(name, msg)); + } + }; + + private final StrCallback roomJoinCb = new StrCallback() { + public void callback(Pointer context, String name) { + sendFromNet(MSG_ROOM_JOIN, name); + } + }; + private final StrStrCallback roomLeaveCb = new StrStrCallback() { + public void callback(Pointer context, String name, String message) { + sendFromNet(MSG_ROOM_LEAVE, Pair.create(name, message)); + } + }; + private final StrStrCallback chatCb = new StrStrCallback() { + public void callback(Pointer context, String name, String msg) { + sendFromNet(MSG_CHAT, Pair.create(name, msg)); + } + }; + + private final IntStrCallback messageCb = new IntStrCallback() { + public void callback(Pointer context, int type, String msg) { + sendFromNet(MSG_MESSAGE, type, msg); + } + }; + + private final RoomCallback roomAddCb = new RoomCallback() { + public void callback(Pointer context, RoomPtr roomPtr) { + sendFromNet(MSG_ROOM_ADD, roomPtr.deref()); + } + }; + + private final StrRoomCallback roomUpdateCb = new StrRoomCallback() { + public void callback(Pointer context, String name, RoomPtr roomPtr) { + sendFromNet(MSG_ROOM_UPDATE, Pair.create(name, roomPtr.deref())); + } + }; + + private final StrCallback roomDeleteCb = new StrCallback() { + public void callback(Pointer context, final String name) { + sendFromNet(MSG_ROOM_DELETE, name); + } + }; + + private final RoomListCallback roomlistCb = new RoomListCallback() { + public void callback(Pointer context, RoomArrayPtr arg1, int count) { + sendFromNet(MSG_ROOMLIST, arg1.getRooms(count)); + } + }; + + private final VoidCallback connectedCb = new VoidCallback() { + public void callback(Pointer context) { + FLIB.flib_netconn_send_request_roomlist(conn); + playerName = FLIB.flib_netconn_get_playername(conn); + sendFromNet(MSG_CONNECTED, playerName); + } + }; + + private final StrCallback passwordRequestCb = new StrCallback() { + public void callback(Pointer context, String nickname) { + sendFromNet(MSG_PASSWORD_REQUEST, playerName); + } + }; + + private final BoolCallback enterRoomCb = new BoolCallback() { + public void callback(Pointer context, boolean isChief) { + sendFromNet(MSG_ENTER_ROOM_FROM_LOBBY, isChief); + } + }; + + private final IntStrCallback leaveRoomCb = new IntStrCallback() { + public void callback(Pointer context, int reason, String message) { + sendFromNet(MSG_LEAVE_ROOM, reason, message); + } + }; + + private final StrBoolCallback readyStateCb = new StrBoolCallback() { + public void callback(Pointer context, String player, boolean ready) { + sendFromNet(MSG_READYSTATE, Pair.create(player, ready)); + } + }; + + private final TeamCallback teamAddedCb = new TeamCallback() { + public void callback(Pointer context, TeamPtr team) { + sendFromNet(MSG_TEAM_ADDED, team.deref().team); + } + }; + + private final StrCallback teamDeletedCb = new StrCallback() { + public void callback(Pointer context, String teamName) { + sendFromNet(MSG_TEAM_DELETED, teamName); + } + }; + + private final StrCallback teamAcceptedCb = new StrCallback() { + public void callback(Pointer context, String teamName) { + sendFromNet(MSG_TEAM_ACCEPTED, teamName); + } + }; + + private final StrIntCallback teamColorChangedCb = new StrIntCallback() { + public void callback(Pointer context, String teamName, int colorIndex) { + sendFromNet(MSG_TEAM_COLOR_CHANGED, colorIndex, teamName); + } + }; + + private final StrIntCallback hogCountChangedCb = new StrIntCallback() { + public void callback(Pointer context, String teamName, int hogCount) { + sendFromNet(MSG_HOG_COUNT_CHANGED, hogCount, teamName); + } + }; + + private final BytesCallback engineMessageCb = new BytesCallback() { + public void callback(Pointer context, Pointer buffer, NativeLong size) { + sendFromNet(MSG_ENGINE_MESSAGE, buffer.getByteArray(0, size.intValue())); + } + }; + + private final VoidCallback runGameCb = new VoidCallback() { + public void callback(Pointer context) { + GameSetupPtr configPtr = FLIB.flib_netconn_create_gamesetup(conn); + sendFromNet(MSG_RUN_GAME, configPtr.deref()); + FLIB.flib_gamesetup_destroy(configPtr); + } + }; + + private void shutdown(boolean error, String message) { + if(conn != null) { + FLIB.flib_netconn_destroy(conn); + conn = null; + } + tickHandler.stop(); + toNetHandler.getLooper().quit(); + sendFromNet(MSG_DISCONNECTED, Pair.create(error, message)); + } + + private final IntStrCallback disconnectCb = new IntStrCallback() { + public void callback(Pointer context, int reason, String message) { + Boolean error = reason != Frontlib.NETCONN_DISCONNECT_NORMAL; + String messageForUser = createDisconnectUserMessage(appContext.getResources(), reason, message); + shutdown(error, messageForUser); + } + }; + + private static String createDisconnectUserMessage(Resources res, int reason, String message) { + switch(reason) { + case Frontlib.NETCONN_DISCONNECT_AUTH_FAILED: + return res.getString(R.string.error_auth_failed); + case Frontlib.NETCONN_DISCONNECT_CONNLOST: + return res.getString(R.string.error_connection_lost); + case Frontlib.NETCONN_DISCONNECT_INTERNAL_ERROR: + return res.getString(R.string.error_unexpected, message); + case Frontlib.NETCONN_DISCONNECT_SERVER_TOO_OLD: + return res.getString(R.string.error_server_too_old); + default: + return message; + } + } + + private boolean sendFromNet(FromNetMsgType what, Object obj) { + return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what.ordinal(), obj)); + } + + private boolean sendFromNet(FromNetMsgType what, int arg1, Object obj) { + return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what.ordinal(), arg1, 0, obj)); + } + + static enum ToNetMsgType { + MSG_SEND_NICK, + MSG_SEND_PASSWORD, + MSG_SEND_QUIT, + MSG_SEND_ROOMLIST_REQUEST, + MSG_SEND_PLAYER_INFO_REQUEST, + MSG_SEND_CHAT, + MSG_SEND_TEAMCHAT, + MSG_SEND_FOLLOW_PLAYER, + MSG_SEND_JOIN_ROOM, + MSG_SEND_CREATE_ROOM, + MSG_SEND_LEAVE_ROOM, + MSG_SEND_KICK, + MSG_SEND_ADD_TEAM, + MSG_SEND_REMOVE_TEAM, + MSG_DISCONNECT, + MSG_SEND_TEAM_COLOR_INDEX, + MSG_SEND_TEAM_HOG_COUNT, + MSG_SEND_ENGINE_MESSAGE, + MSG_SEND_ROUND_FINISHED, + MSG_SEND_TOGGLE_READY, + MSG_SEND_WEAPONSET, + MSG_SEND_MAP, + MSG_SEND_MAP_NAME, + MSG_SEND_MAP_GENERATOR, + MSG_SEND_MAP_TEMPLATE, + MSG_SEND_MAZE_SIZE, + MSG_SEND_MAP_SEED, + MSG_SEND_MAP_THEME, + MSG_SEND_MAP_DRAWDATA, + MSG_SEND_GAMESTYLE, + MSG_SEND_SCHEME; + + static final List<ThreadedNetConnection.ToNetMsgType> values = Collections.unmodifiableList(Arrays.asList(ToNetMsgType.values())); + } + + /** + * Processes messages to the networking system. Runs on a non-main thread. + */ + @SuppressLint("HandlerLeak") + public final class ToNetHandler extends Handler { + + public ToNetHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch(ToNetMsgType.values.get(msg.what)) { + case MSG_SEND_NICK: { + FLIB.flib_netconn_send_nick(conn, (String)msg.obj); + break; + } + case MSG_SEND_PASSWORD: { + FLIB.flib_netconn_send_password(conn, (String)msg.obj); + break; + } + case MSG_SEND_QUIT: { + FLIB.flib_netconn_send_quit(conn, (String)msg.obj); + break; + } + case MSG_SEND_ROOMLIST_REQUEST: { + FLIB.flib_netconn_send_request_roomlist(conn); + break; + } + case MSG_SEND_PLAYER_INFO_REQUEST: { + FLIB.flib_netconn_send_playerInfo(conn, (String)msg.obj); + break; + } + case MSG_SEND_CHAT: { + if(FLIB.flib_netconn_send_chat(conn, (String)msg.obj) == 0) { + sendFromNet(MSG_CHAT, Pair.create(playerName, (String)msg.obj)); + } + break; + } + case MSG_SEND_TEAMCHAT: { + FLIB.flib_netconn_send_teamchat(conn, (String)msg.obj); + break; + } + case MSG_SEND_FOLLOW_PLAYER: { + FLIB.flib_netconn_send_playerFollow(conn, (String)msg.obj); + break; + } + case MSG_SEND_JOIN_ROOM: { + FLIB.flib_netconn_send_joinRoom(conn, (String)msg.obj); + break; + } + case MSG_SEND_CREATE_ROOM: { + FLIB.flib_netconn_send_createRoom(conn, (String)msg.obj); + break; + } + case MSG_SEND_LEAVE_ROOM: { + if(FLIB.flib_netconn_send_leaveRoom(conn, (String)msg.obj) == 0) { + sendFromNet(MSG_LEAVE_ROOM, -1, ""); + } + break; + } + case MSG_SEND_KICK: { + FLIB.flib_netconn_send_kick(conn, (String)msg.obj); + break; + } + case MSG_SEND_ADD_TEAM: { + FLIB.flib_netconn_send_addTeam(conn, TeamPtr.createJavaOwned((Team)msg.obj)); + break; + } + case MSG_SEND_REMOVE_TEAM: { + if(FLIB.flib_netconn_send_removeTeam(conn, (String)msg.obj)==0) { + sendFromNet(MSG_TEAM_DELETED, msg.obj); + } + break; + } + case MSG_DISCONNECT: { + FLIB.flib_netconn_send_quit(conn, (String)msg.obj); + shutdown(false, "User quit"); + break; + } + case MSG_SEND_TEAM_COLOR_INDEX: { + if(FLIB.flib_netconn_send_teamColor(conn, (String)msg.obj, msg.arg1)==0) { + sendFromNet(MSG_TEAM_COLOR_CHANGED, msg.arg1, msg.obj); + } + break; + } + case MSG_SEND_TEAM_HOG_COUNT: { + if(FLIB.flib_netconn_send_teamHogCount(conn, (String)msg.obj, msg.arg1)==0) { + sendFromNet(MSG_HOG_COUNT_CHANGED, msg.arg1, msg.obj); + } + break; + } + case MSG_SEND_ENGINE_MESSAGE: { + byte[] message = (byte[])msg.obj; + Memory mem = new Memory(message.length); + mem.write(0, message, 0, message.length); + FLIB.flib_netconn_send_engineMessage(conn, mem, new NativeLong(message.length)); + break; + } + case MSG_SEND_ROUND_FINISHED: { + FLIB.flib_netconn_send_roundfinished(conn, (Boolean)msg.obj); + break; + } + case MSG_SEND_TOGGLE_READY: { + FLIB.flib_netconn_send_toggleReady(conn); + break; + } + case MSG_SEND_WEAPONSET: { + FLIB.flib_netconn_send_weaponset(conn, WeaponsetPtr.createJavaOwned((Weaponset)msg.obj)); + break; + } + case MSG_SEND_MAP: { + FLIB.flib_netconn_send_map(conn, MapRecipePtr.createJavaOwned((MapRecipe)msg.obj)); + break; + } + case MSG_SEND_MAP_NAME: { + FLIB.flib_netconn_send_mapName(conn, (String)msg.obj); + break; + } + case MSG_SEND_MAP_GENERATOR: { + FLIB.flib_netconn_send_mapGen(conn, msg.arg1); + break; + } + case MSG_SEND_MAP_TEMPLATE: { + FLIB.flib_netconn_send_mapTemplate(conn, msg.arg1); + break; + } + case MSG_SEND_MAZE_SIZE: { + FLIB.flib_netconn_send_mapMazeSize(conn, msg.arg1); + break; + } + case MSG_SEND_MAP_SEED: { + FLIB.flib_netconn_send_mapSeed(conn, (String) msg.obj); + break; + } + case MSG_SEND_MAP_THEME: { + FLIB.flib_netconn_send_mapTheme(conn, (String) msg.obj); + break; + } + case MSG_SEND_MAP_DRAWDATA: { + byte[] message = (byte[])msg.obj; + Memory mem = new Memory(message.length); + mem.write(0, message, 0, message.length); + FLIB.flib_netconn_send_mapDrawdata(conn, mem, new NativeLong(message.length)); + break; + } + case MSG_SEND_GAMESTYLE: { + FLIB.flib_netconn_send_script(conn, (String) msg.obj); + break; + } + case MSG_SEND_SCHEME: { + FLIB.flib_netconn_send_scheme(conn, SchemePtr.createJavaOwned((Scheme) msg.obj)); + break; + } + default: { + Log.e("ToNetHandler", "Unknown message type: "+msg.what); + break; + } + } + } + } +} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TickHandler.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TickHandler.java Sat Aug 18 00:22:33 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -package org.hedgewars.hedgeroid.netplay; - -import android.os.Handler; -import android.os.Looper; -import android.os.Message; - -/** - * This class handles regularly calling a specified runnable - * on the looper provided in the constructor. The first call - * occurs without delay (though still via the looper), all - * following calls are delayed by (approximately) the interval. - * The interval can be changed at any time, which will cause - * an immediate execution of the runnable again. - */ -public class TickHandler extends Handler { - private final Runnable callback; - private int messageId; - private long interval; - private boolean running; - - public TickHandler(Looper looper, long interval, Runnable callback) { - super(looper); - this.callback = callback; - this.interval = interval; - } - - public synchronized void stop() { - messageId++; - running = false; - } - - public synchronized void start() { - messageId++; - sendMessage(obtainMessage(messageId)); - running = true; - } - - public synchronized void setInterval(long interval) { - this.interval = interval; - if(running) { - start(); - } - } - - @Override - public synchronized void handleMessage(Message msg) { - if(msg.what == messageId) { - callback.run(); - } - if(msg.what == messageId) { - sendMessageDelayed(obtainMessage(messageId), interval); - } - } -} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/FileUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/FileUtils.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,265 @@ +/* + * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game + * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + + +package org.hedgewars.hedgeroid.util; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.os.Build; +import android.os.Environment; +import android.util.Log; + +public class FileUtils { + private static final String ROOT_DIR = "Data"; + private static final String TAG = FileUtils.class.getSimpleName(); + + /** + * @return true if the data path is currently available. However, it can vanish at any time so + * normally you should just try to use it and rely on the exceptions. + */ + public static boolean isDataPathAvailable() { + return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); + } + + /** + * get the path to which we should download all the data files + * @param c context + * @return The directory + * @throws FileNotFoundException if external storage is not available at the moment + */ + public static File getCachePath(Context c) throws FileNotFoundException { + File cachePath = null; + if(Build.VERSION.SDK_INT < 8){//8 == Build.VERSION_CODES.FROYO + cachePath = PreFroyoSDCardDir.getDownloadPath(c); + } else { + cachePath = FroyoSDCardDir.getDownloadPath(c); + } + if(cachePath==null) { + throw new FileNotFoundException("External storage is currently unavailable"); + } else { + return cachePath; + } + } + + public static File getDataPathFile(Context c) throws FileNotFoundException { + return new File(getCachePath(c), ROOT_DIR); + } + + // TODO Several callers are unaware that this may fail, so it throws an RTE now. + // Should be handled better though. + @Deprecated + public static String getDataPath(Context c) { + try { + return getDataPathFile(c).getAbsolutePath()+"/"; + } catch(FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + @TargetApi(8) + private static class FroyoSDCardDir{ + public static File getDownloadPath(Context c){ + return c.getExternalCacheDir(); + } + } + + private static class PreFroyoSDCardDir{ + public static File getDownloadPath(Context c){ + if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ + File extStorageDir = Environment.getExternalStorageDirectory(); + if(extStorageDir != null) { + return new File(extStorageDir, "Hedgewars"); + } + } + return null; + } + } + + /** + * Return a File array with all the files from dirName + * @param c + * @param dirName + * @return + * @throws FileNotFoundException If the sdcard is not available or the subdirectory "dirName" does not exist + */ + public static File[] getFilesFromRelativeDir(Context c, String dirName) throws FileNotFoundException { + File f = new File(getDataPathFile(c), dirName); + + if(f.isDirectory()) { + return f.listFiles(); + } else { + throw new FileNotFoundException("Directory "+dirName+" does not exist."); + } + } + + /** + * Checks if this directory has a file with suffix suffix + * @param f - directory + * @return + */ + public static boolean hasFileWithSuffix(File f, String suffix){ + if(f.isDirectory()){ + for(String s : f.list()){ + if(s.endsWith(suffix)) return true; + } + return false; + }else{ + return false; + } + } + + /** + * Gives back all dirs which contain a file with suffix fileSuffix + * @param c + * @param path + * @param fileSuffix + * @return + * @throws FileNotFoundException If the sdcard is not available or the subdirectory "path" does not exist + */ + public static List<String> getDirsWithFileSuffix(Context c, String path, String fileSuffix) throws FileNotFoundException{ + File[] files = getFilesFromRelativeDir(c,path); + ArrayList<String> ret = new ArrayList<String>(); + + for(File f : files){ + if(hasFileWithSuffix(f, fileSuffix)) ret.add(f.getName()); + } + return ret; + } + + /** + * Get all files from directory dir which have the given suffix + * @throws FileNotFoundException If the sdcard is not available or the subdirectory "dir" does not exist + */ + public static ArrayList<String> getFileNamesFromDirWithSuffix(Context c, String dir, String suffix, boolean removeSuffix) throws FileNotFoundException{ + File[] files = FileUtils.getFilesFromRelativeDir(c, dir); + ArrayList<String> ret = new ArrayList<String>(); + for(File file : files){ + String s = file.getName(); + if(s.endsWith(suffix)){ + if(removeSuffix) ret.add(s.substring(0, s.length()-suffix.length())); + else ret.add(s); + } + } + return ret; + } + + /** + * Close a resource (possibly null), ignoring any IOException. + */ + public static void closeQuietly(Closeable c) { + if(c!=null) { + try { + c.close(); + } catch(IOException e) { + Log.w(TAG, e); + } + } + } + + /** + * Write all data from the input stream to the file, creating or overwriting it. + * The input stream will be closed. + * + * @throws IOException + */ + public static void writeStreamToFile(InputStream is, File file) throws IOException { + OutputStream os = null; + byte[] buffer = new byte[8192]; + try { + os = new FileOutputStream(file); + int size; + while((size=is.read(buffer)) != -1) { + os.write(buffer, 0, size); + } + os.close(); // Important to close this non-quietly, in case of exceptions when flushing + } finally { + FileUtils.closeQuietly(is); + FileUtils.closeQuietly(os); + } + } + + /** + * Moves resources pointed to by sourceResId (from @res/raw/) to the app's private data directory + * @param c + * @param sourceResId + * @param directory + */ + public static void resRawToFilesDir(Context c, int sourceResId, int targetFilenames, String directory) throws IOException { + File targetDir = new File(c.getFilesDir(), directory); + targetDir.mkdirs(); + + //Get an array with the resource files ID + Resources resources = c.getResources(); + TypedArray ta = resources.obtainTypedArray(sourceResId); + TypedArray filenames = resources.obtainTypedArray(targetFilenames); + for(int i = 0; i < ta.length(); i++){ + int resId = ta.getResourceId(i, 0); + String fileName = filenames.getString(i); + File f = new File(targetDir, fileName); + writeStreamToFile(resources.openRawResource(resId), f); + } + } + + public static String readToString(InputStream is) throws IOException { + try { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int size; + while((size=is.read(buffer)) != -1) { + os.write(buffer, 0, size); + } + return new String(os.toByteArray()); + } finally { + closeQuietly(is); + } + } + + private static final char[] badFilenameChars = new char[] { '/', '\\', ':', '*', '?', '\"', '<', '>', '|', '.', '\0' }; + + /** + * Modify the given String so that it can be used as part of a filename + * without causing problems from illegal/special characters. + * + * The result should be similar to the input, but isn't necessarily + * reversible. + */ + public static String replaceBadChars(String name) { + if (name == null || name.trim().length()==0) { + return "_"; + } + name = name.trim(); + for (char badChar : badFilenameChars) { + name = name.replace(badChar, '_'); + } + return name; + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/ObservableTreeMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/ObservableTreeMap.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,43 @@ +package org.hedgewars.hedgeroid.util; + +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; + +import android.database.DataSetObservable; + +public class ObservableTreeMap<K,V> extends DataSetObservable { + private final Map<K, V> map = new TreeMap<K, V>(); + + public void replaceContent(Map<? extends K, ? extends V> newMap) { + map.clear(); + map.putAll(newMap); + notifyChanged(); + } + + public void put(K key, V value) { + map.put(key, value); + notifyChanged(); + } + + public V get(K key) { + return map.get(key); + } + + public void remove(K key) { + if(map.remove(key) != null) { + notifyChanged(); + } + } + + public void clear() { + if(!map.isEmpty()) { + map.clear(); + notifyChanged(); + } + } + + public Map<K, V> getMap() { + return Collections.unmodifiableMap(map); + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/ObservableTreeMapAdapter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/ObservableTreeMapAdapter.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,75 @@ +package org.hedgewars.hedgeroid.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import android.database.DataSetObserver; +import android.widget.BaseAdapter; + +public abstract class ObservableTreeMapAdapter<K,V> extends BaseAdapter { + private boolean sourceChanged = true; + private List<V> entries = new ArrayList<V>(); + private ObservableTreeMap<K, V> source; + + private DataSetObserver observer = new DataSetObserver() { + @Override + public void onChanged() { + sourceChanged = true; + notifyDataSetChanged(); + } + + @Override + public void onInvalidated() { + invalidate(); + } + }; + + abstract protected Comparator<V> getEntryOrder(); + + protected List<V> getEntries() { + if(sourceChanged) { + entries.clear(); + entries.addAll(source.getMap().values()); + Collections.sort(entries, getEntryOrder()); + sourceChanged = false; + } + return entries; + } + + public int getCount() { + return getEntries().size(); + } + + public V getItem(int position) { + return getEntries().get(position); + } + + public long getItemId(int position) { + return position; + } + + @Override + public boolean hasStableIds() { + return false; + } + + public void setSource(ObservableTreeMap<K,V> source) { + if(this.source != null) { + this.source.unregisterObserver(observer); + } + this.source = source; + this.source.registerObserver(observer); + sourceChanged = true; + notifyDataSetChanged(); + } + + public void invalidate() { + if(source != null) { + source.unregisterObserver(observer); + } + source = null; + notifyDataSetInvalidated(); + } +} \ No newline at end of file diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/TextInputDialog.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/TextInputDialog.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,128 @@ +package org.hedgewars.hedgeroid.util; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.view.KeyEvent; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; + +/** + * A generic text input dialog with configurable text. The Activity must implement the callback + * interface TextInputDialogListener, which will be called by the dialog if it is submitted or cancelled. + */ +public class TextInputDialog extends DialogFragment { + private static final String BUNDLE_DIALOG_ID = "dialogId"; + private static final String BUNDLE_TITLE_TEXT = "title"; + private static final String BUNDLE_MESSAGE_TEXT = "message"; + private static final String BUNDLE_HINT_TEXT = "hint"; + + private int dialogId, titleText, messageText, hintText; + private TextInputDialogListener listener; + + public interface TextInputDialogListener { + void onTextInputDialogSubmitted(int dialogId, String text); + void onTextInputDialogCancelled(int dialogId); + } + + /** + * The dialogId is only used for passing back to the callback on the activity, the + * other parameters are text resource IDs. Pass 0 for any of them to not use this + * text. + */ + public TextInputDialog(int dialogId, int titleText, int messageText, int hintText) { + this.dialogId = dialogId; + this.titleText = titleText; + this.messageText = messageText; + this.hintText = hintText; + } + + public TextInputDialog() { + // Only for reflection-based instantiation by the framework + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + listener = (TextInputDialogListener) activity; + } catch(ClassCastException e) { + throw new ClassCastException("Activity " + activity + " must implement TextInputDialogListener to use TextInputDialog."); + } + } + + @Override + public void onDetach() { + super.onDetach(); + listener = null; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + if(savedInstanceState != null) { + dialogId = savedInstanceState.getInt(BUNDLE_DIALOG_ID, dialogId); + titleText = savedInstanceState.getInt(BUNDLE_TITLE_TEXT, titleText); + messageText = savedInstanceState.getInt(BUNDLE_MESSAGE_TEXT, messageText); + hintText = savedInstanceState.getInt(BUNDLE_HINT_TEXT, hintText); + } + + final EditText editText = new EditText(getActivity()); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + + if(titleText != 0) { + builder.setTitle(titleText); + } + if(messageText != 0) { + builder.setTitle(messageText); + } + if(hintText != 0) { + editText.setHint(hintText); + } + + editText.setId(android.R.id.text1); + editText.setImeOptions(EditorInfo.IME_ACTION_DONE); + editText.setSingleLine(); + + builder.setView(editText); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + + editText.setOnEditorActionListener(new OnEditorActionListener() { + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + listener.onTextInputDialogSubmitted(dialogId, v.getText().toString()); + return true; + } + }); + + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + listener.onTextInputDialogSubmitted(dialogId, editText.getText().toString()); + } + }); + + return builder.create(); + } + + @Override + public void onSaveInstanceState(Bundle icicle) { + super.onSaveInstanceState(icicle); + icicle.putInt(BUNDLE_DIALOG_ID, dialogId); + icicle.putInt(BUNDLE_TITLE_TEXT, titleText); + icicle.putInt(BUNDLE_MESSAGE_TEXT, messageText); + icicle.putInt(BUNDLE_HINT_TEXT, hintText); + } + + @Override + public void onCancel(DialogInterface dialog) { + super.onCancel(dialog); + listener.onTextInputDialogCancelled(dialogId); + } +} diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/TickHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/TickHandler.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,54 @@ +package org.hedgewars.hedgeroid.util; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +/** + * This class handles regularly calling a specified runnable + * on the looper provided in the constructor. The first call + * occurs without delay (though still via the looper), all + * following calls are delayed by (approximately) the interval. + * The interval can be changed at any time, which will cause + * an immediate execution of the runnable again. + */ +public class TickHandler extends Handler { + private final Runnable callback; + private int messageId; + private long interval; + private boolean running; + + public TickHandler(Looper looper, long interval, Runnable callback) { + super(looper); + this.callback = callback; + this.interval = interval; + } + + public synchronized void stop() { + messageId++; + running = false; + } + + public synchronized void start() { + messageId++; + sendMessage(obtainMessage(messageId)); + running = true; + } + + public synchronized void setInterval(long interval) { + this.interval = interval; + if(running) { + start(); + } + } + + @Override + public synchronized void handleMessage(Message msg) { + if(msg.what == messageId) { + callback.run(); + } + if(msg.what == messageId) { + sendMessageDelayed(obtainMessage(messageId), interval); + } + } +} \ No newline at end of file