diff --git a/core/assets-raw/sprites/ui/icons/icon-admin.png b/core/assets-raw/sprites/ui/icons/icon-admin.png new file mode 100644 index 0000000000..3d3811462a Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-admin.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-ban.png b/core/assets-raw/sprites/ui/icons/icon-ban.png new file mode 100644 index 0000000000..f8df32c20c Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-ban.png differ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index eb860705bf..cd22419d6a 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -25,6 +25,7 @@ text.server.kicked.kick=You have been kicked from the server! text.server.kicked.invalidPassword=Invalid password! text.server.kicked.clientOutdated=Outdated client! Update your game! text.server.kicked.serverOutdated=Outdated server! Ask the host to update! +text.server.kicked.banned=You are banned on this server. text.server.connected={0} has joined. text.server.disconnected={0} has disconnected. text.nohost=Can't host server on a custom map! @@ -37,10 +38,18 @@ text.server.refreshing=Refreshing server text.hosts.none=[lightgray]No LAN games found! text.host.invalid=[scarlet]Can't connect to host. text.server.friendlyfire=Friendly Fire +text.server.bans=Bans +text.server.bans.none=No banned players found! +text.server.admins=Admins +text.server.admins.none=No admins found! text.server.add=Add Server text.server.delete=Are you sure you want to delete this server? text.server.hostname=Host: {0} text.server.edit=Edit Server +text.confirmban=Are you sure you want to ban this player? +text.confirmunban=Are you sure you want to unban this player? +text.confirmadmin=Are you sure you want to make this player an admin? +text.confirmunadmin=Are you sure you want to remove admin status from this player? text.joingame.byip=Join by IP... text.joingame.title=Join Game text.joingame.ip=IP: diff --git a/core/assets/sprites/sprites.atlas b/core/assets/sprites/sprites.atlas index 40c8ead303..77a8a23490 100644 --- a/core/assets/sprites/sprites.atlas +++ b/core/assets/sprites/sprites.atlas @@ -27,49 +27,49 @@ blocks/blackrock1 index: -1 blocks/blackrockshadow1 rotate: false - xy: 214, 87 + xy: 732, 381 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstone1 rotate: false - xy: 224, 87 + xy: 736, 371 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstone2 rotate: false - xy: 732, 381 + xy: 736, 361 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstone3 rotate: false - xy: 234, 87 + xy: 736, 351 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstoneblock1 rotate: false - xy: 736, 371 + xy: 540, 110 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstoneblock2 rotate: false - xy: 736, 361 + xy: 659, 282 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstoneblock3 rotate: false - xy: 736, 351 + xy: 669, 282 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -83,7 +83,7 @@ blocks/blackstoneedge index: -1 blocks/block rotate: false - xy: 264, 91 + xy: 250, 91 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -104,7 +104,7 @@ blocks/block-3x3 index: -1 blocks/block-middle rotate: false - xy: 274, 91 + xy: 260, 91 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -125,112 +125,112 @@ blocks/chainturret-icon index: -1 blocks/coal1 rotate: false - xy: 314, 95 + xy: 300, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coal2 rotate: false - xy: 324, 95 + xy: 310, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coal3 rotate: false - xy: 334, 95 + xy: 320, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coaldrill rotate: false - xy: 344, 95 + xy: 330, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coalgenerator rotate: false - xy: 354, 95 + xy: 340, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coalgenerator-top rotate: false - xy: 364, 95 + xy: 350, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coalpurifier rotate: false - xy: 374, 95 + xy: 360, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/combustiongenerator rotate: false - xy: 384, 95 + xy: 370, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/compositewall rotate: false - xy: 394, 95 + xy: 380, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conduit rotate: false - xy: 404, 95 + xy: 390, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conduitbottom rotate: false - xy: 414, 95 + xy: 400, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conduitliquid rotate: false - xy: 424, 95 + xy: 410, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conduittop rotate: false - xy: 434, 95 + xy: 420, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conveyor rotate: false - xy: 444, 95 + xy: 430, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conveyormove rotate: false - xy: 454, 95 + xy: 440, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conveyortunnel rotate: false - xy: 464, 95 + xy: 450, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -244,56 +244,56 @@ blocks/core index: -1 blocks/cross rotate: false - xy: 669, 282 + xy: 460, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/crucible rotate: false - xy: 679, 282 + xy: 470, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/deepwater rotate: false - xy: 591, 187 + xy: 601, 187 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/dirt1 rotate: false - xy: 624, 233 + xy: 611, 187 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/dirt2 rotate: false - xy: 624, 223 + xy: 621, 189 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/dirt3 rotate: false - xy: 624, 213 + xy: 114, 80 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/dirtedge rotate: false - xy: 521, 164 + xy: 535, 211 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/door rotate: false - xy: 634, 235 + xy: 113, 70 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -307,7 +307,7 @@ blocks/door-large index: -1 blocks/door-large-icon rotate: false - xy: 634, 225 + xy: 113, 60 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -321,7 +321,7 @@ blocks/door-large-open index: -1 blocks/door-open rotate: false - xy: 634, 215 + xy: 113, 50 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -335,7 +335,7 @@ blocks/doubleturret index: -1 blocks/duriumwall rotate: false - xy: 114, 80 + xy: 117, 40 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -349,14 +349,14 @@ blocks/duriumwall-large index: -1 blocks/duriumwall-large-icon rotate: false - xy: 113, 70 + xy: 117, 30 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/enemyspawn rotate: false - xy: 113, 60 + xy: 117, 20 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -370,245 +370,245 @@ blocks/flameturret index: -1 blocks/fluxpump rotate: false - xy: 113, 50 + xy: 117, 10 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grass1 rotate: false - xy: 117, 40 + xy: 865, 417 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grass2 rotate: false - xy: 117, 30 + xy: 624, 233 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grass3 rotate: false - xy: 117, 20 + xy: 624, 223 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grassblock1 rotate: false - xy: 117, 10 + xy: 624, 213 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grassblock2 rotate: false - xy: 865, 417 + xy: 634, 235 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grassedge rotate: false - xy: 521, 150 + xy: 535, 197 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/ice1 rotate: false - xy: 178, 75 + xy: 634, 225 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/ice2 rotate: false - xy: 188, 75 + xy: 634, 215 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/ice3 rotate: false - xy: 198, 75 + xy: 178, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/iceedge rotate: false - xy: 521, 136 + xy: 549, 213 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/icerock1 rotate: false - xy: 208, 75 + xy: 188, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/icerock2 rotate: false - xy: 218, 77 + xy: 198, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/icerockshadow1 rotate: false - xy: 228, 77 + xy: 208, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/rockshadow1 rotate: false - xy: 228, 77 + xy: 208, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/icerockshadow2 rotate: false - xy: 238, 77 + xy: 218, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/rockshadow2 rotate: false - xy: 238, 77 + xy: 218, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/iron1 rotate: false - xy: 328, 85 + xy: 248, 71 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/iron2 rotate: false - xy: 338, 85 + xy: 258, 71 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/iron3 rotate: false - xy: 348, 85 + xy: 268, 71 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/irondrill rotate: false - xy: 358, 85 + xy: 278, 71 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/ironwall rotate: false - xy: 368, 85 + xy: 288, 71 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/junction rotate: false - xy: 378, 85 + xy: 537, 140 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/laserturret rotate: false - xy: 728, 333 + xy: 853, 415 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/lava rotate: false - xy: 388, 85 + xy: 300, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/lavaedge rotate: false - xy: 518, 122 + xy: 563, 213 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/lavasmelter rotate: false - xy: 398, 85 + xy: 310, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/liquiditemjunction rotate: false - xy: 408, 85 + xy: 320, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/liquidjunction rotate: false - xy: 418, 85 + xy: 330, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/liquidrouter rotate: false - xy: 428, 85 + xy: 340, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/machineturret rotate: false - xy: 728, 321 + xy: 619, 199 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/megarepairturret rotate: false - xy: 728, 309 + xy: 142, 81 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/mortarturret rotate: false - xy: 731, 297 + xy: 154, 81 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/mossblock rotate: false - xy: 438, 85 + xy: 350, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/mossstone rotate: false - xy: 438, 85 + xy: 350, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -629,7 +629,7 @@ blocks/nuclearreactor-center index: -1 blocks/nuclearreactor-icon rotate: false - xy: 448, 85 + xy: 360, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -650,336 +650,336 @@ blocks/nuclearreactor-small index: -1 blocks/oil rotate: false - xy: 458, 85 + xy: 370, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/oiledge rotate: false - xy: 535, 197 + xy: 577, 207 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/oilrefinery rotate: false - xy: 468, 85 + xy: 380, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/omnidrill rotate: false - xy: 601, 187 + xy: 390, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/plasmaturret rotate: false - xy: 731, 285 + xy: 166, 81 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/playerspawn rotate: false - xy: 611, 189 + xy: 400, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/powerbooster rotate: false - xy: 621, 189 + xy: 410, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/poweredconveyor rotate: false - xy: 875, 417 + xy: 420, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/poweredconveyormove rotate: false - xy: 885, 421 + xy: 430, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/powerlaser rotate: false - xy: 895, 421 + xy: 440, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/powerlasercorner rotate: false - xy: 905, 421 + xy: 450, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/powerlaserrouter rotate: false - xy: 915, 421 + xy: 460, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/pulseconduit rotate: false - xy: 925, 421 + xy: 470, 85 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/pulseconduitbottom rotate: false - xy: 935, 421 + xy: 480, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/pulseconduittop rotate: false - xy: 945, 421 + xy: 298, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/pump rotate: false - xy: 955, 421 + xy: 308, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/repairturret rotate: false - xy: 853, 415 + xy: 178, 85 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/rock1 rotate: false - xy: 965, 421 + xy: 318, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/rock2 rotate: false - xy: 975, 421 + xy: 328, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/router rotate: false - xy: 985, 421 + xy: 338, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/rtgenerator rotate: false - xy: 995, 421 + xy: 348, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/rtgenerator-top rotate: false - xy: 248, 71 + xy: 358, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sand1 rotate: false - xy: 258, 71 + xy: 368, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sand2 rotate: false - xy: 268, 71 + xy: 378, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sand3 rotate: false - xy: 278, 71 + xy: 388, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sandblock1 rotate: false - xy: 288, 71 + xy: 398, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sandblock2 rotate: false - xy: 298, 71 + xy: 408, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sandblock3 rotate: false - xy: 308, 75 + xy: 418, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sandedge rotate: false - xy: 549, 213 + xy: 577, 193 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/shadow rotate: false - xy: 142, 81 + xy: 190, 85 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/shieldgenerator rotate: false - xy: 328, 75 + xy: 438, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/shotgunturret rotate: false - xy: 154, 81 + xy: 202, 85 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/shrub rotate: false - xy: 378, 75 + xy: 551, 168 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/shrubshadow rotate: false - xy: 388, 75 + xy: 551, 158 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/smelter rotate: false - xy: 398, 75 + xy: 551, 148 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/smelter-middle rotate: false - xy: 408, 75 + xy: 875, 417 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sniperturret rotate: false - xy: 166, 81 + xy: 214, 85 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/snow1 rotate: false - xy: 418, 75 + xy: 885, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/snow2 rotate: false - xy: 428, 75 + xy: 895, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/snow3 rotate: false - xy: 438, 75 + xy: 905, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/snowblock1 rotate: false - xy: 448, 75 + xy: 915, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/snowblock2 rotate: false - xy: 458, 75 + xy: 925, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/snowblock3 rotate: false - xy: 468, 75 + xy: 935, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/snowedge rotate: false - xy: 549, 199 + xy: 605, 197 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/sorter rotate: false - xy: 478, 87 + xy: 945, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/steelconveyor rotate: false - xy: 488, 87 + xy: 955, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/steelconveyormove rotate: false - xy: 478, 77 + xy: 965, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/steelwall rotate: false - xy: 488, 77 + xy: 975, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -993,98 +993,98 @@ blocks/steelwall-large index: -1 blocks/steelwall-large-icon rotate: false - xy: 498, 80 + xy: 985, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stone1 rotate: false - xy: 508, 80 + xy: 995, 421 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stone2 rotate: false - xy: 885, 411 + xy: 298, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stone3 rotate: false - xy: 895, 411 + xy: 308, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stoneblock1 rotate: false - xy: 905, 411 + xy: 318, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stoneblock2 rotate: false - xy: 915, 411 + xy: 328, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stoneblock3 rotate: false - xy: 925, 411 + xy: 338, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stonedrill rotate: false - xy: 935, 411 + xy: 348, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stoneedge rotate: false - xy: 577, 193 + xy: 537, 150 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/stoneformer rotate: false - xy: 945, 411 + xy: 358, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stonewall rotate: false - xy: 955, 411 + xy: 368, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/teleporter rotate: false - xy: 965, 411 + xy: 378, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/teleporter-top rotate: false - xy: 975, 411 + xy: 388, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/thermalgenerator rotate: false - xy: 985, 411 + xy: 398, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -1098,56 +1098,56 @@ blocks/titancannon index: -1 blocks/titancannon-icon rotate: false - xy: 178, 85 + xy: 226, 85 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/titanium1 rotate: false - xy: 995, 411 + xy: 408, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titanium2 rotate: false - xy: 308, 65 + xy: 418, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titanium3 rotate: false - xy: 318, 65 + xy: 428, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titaniumdrill rotate: false - xy: 328, 65 + xy: 438, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titaniumpurifier rotate: false - xy: 338, 65 + xy: 448, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titaniumshieldwall rotate: false - xy: 348, 65 + xy: 458, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titaniumwall rotate: false - xy: 358, 65 + xy: 468, 65 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -1161,63 +1161,63 @@ blocks/titaniumwall-large index: -1 blocks/titaniumwall-large-icon rotate: false - xy: 368, 65 + xy: 557, 178 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/turret rotate: false - xy: 190, 85 + xy: 528, 108 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/uranium1 rotate: false - xy: 408, 65 + xy: 561, 168 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/uranium2 rotate: false - xy: 418, 65 + xy: 561, 158 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/uranium3 rotate: false - xy: 428, 65 + xy: 561, 148 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/uraniumdrill rotate: false - xy: 438, 65 + xy: 571, 169 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/water rotate: false - xy: 468, 65 + xy: 547, 138 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/wateredge rotate: false - xy: 591, 197 + xy: 521, 132 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/waveturret rotate: false - xy: 202, 85 + xy: 533, 120 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -1231,21 +1231,21 @@ blocks/weaponfactory index: -1 blocks/weaponfactory-icon rotate: false - xy: 478, 67 + xy: 557, 138 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 bullet rotate: false - xy: 284, 91 + xy: 270, 91 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 chainbullet rotate: false - xy: 659, 283 + xy: 591, 188 size: 8, 7 orig: 8, 7 offset: 0, 0 @@ -1392,91 +1392,91 @@ enemies/healerenemy-t3 index: -1 enemies/mortarenemy-t1 rotate: false - xy: 965, 447 + xy: 981, 447 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/mortarenemy-t2 rotate: false - xy: 965, 431 + xy: 981, 431 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/mortarenemy-t3 rotate: false - xy: 981, 447 + xy: 997, 447 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/rapidenemy-t1 rotate: false - xy: 981, 431 + xy: 997, 431 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/rapidenemy-t2 rotate: false - xy: 997, 447 + xy: 592, 211 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/rapidenemy-t3 rotate: false - xy: 997, 431 + xy: 608, 211 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/standardenemy-t1 rotate: false - xy: 592, 211 + xy: 519, 208 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/targetenemy-t1 rotate: false - xy: 592, 211 + xy: 519, 208 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/standardenemy-t2 rotate: false - xy: 563, 199 + xy: 533, 178 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 enemies/standardenemy-t3 rotate: false - xy: 577, 207 + xy: 537, 164 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 enemies/tankenemy-t1 rotate: false - xy: 608, 211 + xy: 519, 192 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/tankenemy-t2 rotate: false - xy: 519, 208 + xy: 521, 162 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/tankenemy-t3 rotate: false - xy: 519, 192 + xy: 521, 146 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -1511,63 +1511,63 @@ enemyarrow index: -1 icon-coal rotate: false - xy: 248, 81 + xy: 228, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-dirium rotate: false - xy: 258, 81 + xy: 238, 81 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-iron rotate: false - xy: 268, 81 + xy: 248, 81 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-sand rotate: false - xy: 278, 81 + xy: 258, 81 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-steel rotate: false - xy: 288, 81 + xy: 268, 81 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-stone rotate: false - xy: 298, 81 + xy: 278, 81 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-titanium rotate: false - xy: 308, 85 + xy: 288, 81 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-uranium rotate: false - xy: 318, 85 + xy: 238, 71 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 laser rotate: false - xy: 533, 178 + xy: 518, 126 size: 1, 12 orig: 1, 12 offset: 0, 0 @@ -1588,49 +1588,49 @@ laserfull index: -1 mechs/mech-standard rotate: false - xy: 516, 108 + xy: 549, 199 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 mechs/mech-standard-icon rotate: false - xy: 535, 211 + xy: 563, 199 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 mechs/ship-standard rotate: false - xy: 563, 213 + xy: 591, 197 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 shell rotate: false - xy: 318, 75 + xy: 428, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 shot rotate: false - xy: 358, 75 + xy: 468, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 shot-long rotate: false - xy: 368, 75 + xy: 547, 178 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 titanshell rotate: false - xy: 378, 65 + xy: 567, 189 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -1762,6 +1762,13 @@ ui/icons/icon-add orig: 14, 14 offset: 0, 0 index: -1 +ui/icons/icon-admin + rotate: false + xy: 977, 463 + size: 14, 14 + orig: 14, 14 + offset: 0, 0 + index: -1 ui/icons/icon-areaDelete rotate: false xy: 93, 2 @@ -1771,7 +1778,7 @@ ui/icons/icon-areaDelete index: -1 ui/icons/icon-arrow rotate: false - xy: 977, 463 + xy: 993, 479 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -1811,23 +1818,30 @@ ui/icons/icon-back orig: 16, 16 offset: 0, 0 index: -1 +ui/icons/icon-ban + rotate: false + xy: 1009, 479 + size: 14, 14 + orig: 14, 14 + offset: 0, 0 + index: -1 ui/icons/icon-cancel rotate: false - xy: 993, 479 + xy: 993, 463 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-chat rotate: false - xy: 605, 199 + xy: 142, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-check rotate: false - xy: 1009, 479 + xy: 1009, 463 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -1855,63 +1869,63 @@ ui/icons/icon-close-over index: -1 ui/icons/icon-crafting rotate: false - xy: 617, 199 + xy: 154, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-cursor rotate: false - xy: 142, 93 + xy: 166, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-defense rotate: false - xy: 154, 93 + xy: 521, 120 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-discord rotate: false - xy: 993, 463 + xy: 592, 227 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-distribution rotate: false - xy: 166, 93 + xy: 516, 108 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-donate rotate: false - xy: 1009, 463 + xy: 608, 227 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-dots rotate: false - xy: 592, 227 + xy: 500, 106 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-editor rotate: false - xy: 608, 227 + xy: 482, 97 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-file-text rotate: false - xy: 500, 106 + xy: 498, 90 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -1925,21 +1939,21 @@ ui/icons/icon-fill index: -1 ui/icons/icon-floppy rotate: false - xy: 482, 97 + xy: 628, 245 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-folder rotate: false - xy: 498, 90 + xy: 837, 438 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-folder-parent rotate: false - xy: 628, 245 + xy: 837, 422 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -1953,35 +1967,35 @@ ui/icons/icon-grid index: -1 ui/icons/icon-hold rotate: false - xy: 536, 185 + xy: 722, 415 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-holdDelete rotate: false - xy: 536, 173 + xy: 722, 403 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-home rotate: false - xy: 837, 438 + xy: 853, 443 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-host rotate: false - xy: 837, 422 + xy: 853, 427 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-info rotate: false - xy: 535, 161 + xy: 722, 391 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -1995,7 +2009,7 @@ ui/icons/icon-line index: -1 ui/icons/icon-load rotate: false - xy: 853, 443 + xy: 869, 446 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -2023,21 +2037,21 @@ ui/icons/icon-loading index: -1 ui/icons/icon-menu rotate: false - xy: 535, 149 + xy: 734, 416 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-none rotate: false - xy: 535, 137 + xy: 734, 404 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-pause rotate: false - xy: 722, 415 + xy: 734, 392 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -2051,7 +2065,7 @@ ui/icons/icon-pencil index: -1 ui/icons/icon-pencil-small rotate: false - xy: 853, 427 + xy: 869, 430 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -2065,42 +2079,42 @@ ui/icons/icon-pick index: -1 ui/icons/icon-play rotate: false - xy: 722, 403 + xy: 724, 369 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-play-2 rotate: false - xy: 869, 446 + xy: 885, 447 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-players rotate: false - xy: 722, 391 + xy: 724, 357 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-power rotate: false - xy: 734, 416 + xy: 724, 345 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-production rotate: false - xy: 734, 404 + xy: 728, 333 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-quit rotate: false - xy: 869, 430 + xy: 885, 431 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -2114,14 +2128,14 @@ ui/icons/icon-redo index: -1 ui/icons/icon-refresh rotate: false - xy: 885, 447 + xy: 901, 447 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-rename rotate: false - xy: 885, 431 + xy: 901, 431 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -2135,35 +2149,35 @@ ui/icons/icon-resize index: -1 ui/icons/icon-rotate rotate: false - xy: 901, 447 + xy: 917, 447 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-rotate-arrow rotate: false - xy: 901, 431 + xy: 917, 431 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-rotate-left rotate: false - xy: 917, 447 + xy: 933, 447 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-rotate-right rotate: false - xy: 917, 431 + xy: 933, 431 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-save rotate: false - xy: 933, 447 + xy: 949, 447 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -2184,7 +2198,7 @@ ui/icons/icon-save-map index: -1 ui/icons/icon-settings rotate: false - xy: 734, 392 + xy: 728, 321 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -2198,28 +2212,28 @@ ui/icons/icon-terrain index: -1 ui/icons/icon-tools rotate: false - xy: 933, 431 + xy: 949, 431 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-touch rotate: false - xy: 724, 369 + xy: 728, 309 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-touchDelete rotate: false - xy: 724, 357 + xy: 731, 297 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-trash rotate: false - xy: 949, 447 + xy: 965, 447 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -2233,7 +2247,7 @@ ui/icons/icon-trash-16 index: -1 ui/icons/icon-tutorial rotate: false - xy: 949, 431 + xy: 965, 431 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -2247,7 +2261,7 @@ ui/icons/icon-undo index: -1 ui/icons/icon-weapon rotate: false - xy: 724, 345 + xy: 731, 285 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -2332,7 +2346,7 @@ ui/selection index: -1 ui/slider rotate: false - xy: 474, 95 + xy: 634, 277 size: 1, 8 orig: 1, 8 offset: 0, 0 @@ -2441,70 +2455,70 @@ weapons/beam-equip index: -1 weapons/blaster rotate: false - xy: 244, 91 + xy: 679, 282 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/blaster-equip rotate: false - xy: 254, 91 + xy: 240, 91 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/clustergun rotate: false - xy: 294, 91 + xy: 280, 91 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/clustergun-equip rotate: false - xy: 304, 95 + xy: 290, 91 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/shockgun rotate: false - xy: 338, 75 + xy: 448, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/shockgun-equip rotate: false - xy: 348, 75 + xy: 458, 75 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/triblaster rotate: false - xy: 388, 65 + xy: 567, 179 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/triblaster-equip rotate: false - xy: 398, 65 + xy: 577, 183 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/vulcan rotate: false - xy: 448, 65 + xy: 571, 159 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/vulcan-equip rotate: false - xy: 458, 65 + xy: 571, 149 size: 8, 8 orig: 8, 8 offset: 0, 0 diff --git a/core/assets/sprites/sprites.png b/core/assets/sprites/sprites.png index 6193c11acd..05ff91f4a1 100644 Binary files a/core/assets/sprites/sprites.png and b/core/assets/sprites/sprites.png differ diff --git a/core/assets/version.properties b/core/assets/version.properties index 0013aa0d32..ce3e39f173 100644 --- a/core/assets/version.properties +++ b/core/assets/version.properties @@ -1,7 +1,7 @@ #Autogenerated file. Do not modify. -#Fri Feb 23 21:37:45 EST 2018 +#Sun Feb 25 22:24:39 EST 2018 version=release -androidBuildCode=275 +androidBuildCode=282 name=Mindustry code=3.3 build=custom build diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 13b9b687b1..5e53b9cd44 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -17,7 +17,9 @@ import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.NetworkIO; import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.resource.Item; +import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Map; +import io.anuke.mindustry.world.Placement; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.ProductionBlocks; import io.anuke.ucore.core.Timers; @@ -165,6 +167,14 @@ public class NetClient extends Module { ui.hudfrag.updateItems(); }); + Net.handleClient(PlacePacket.class, (packet) -> { + Placement.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false); + }); + + Net.handleClient(BreakPacket.class, (packet) -> { + Placement.breakBlock(packet.x, packet.y, true, false); + }); + Net.handleClient(EntitySpawnPacket.class, packet -> { EntityGroup group = packet.group; @@ -316,6 +326,11 @@ public class NetClient extends Module { r.run(); } }); + + Net.handleClient(NetErrorPacket.class, packet -> { + ui.showError(packet.message); + disconnectQuietly(); + }); } @Override diff --git a/core/src/io/anuke/mindustry/core/NetCommon.java b/core/src/io/anuke/mindustry/core/NetCommon.java index b2326211cd..9b8a9435da 100644 --- a/core/src/io/anuke/mindustry/core/NetCommon.java +++ b/core/src/io/anuke/mindustry/core/NetCommon.java @@ -4,11 +4,8 @@ import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Packets.*; -import io.anuke.mindustry.resource.Recipe; -import io.anuke.mindustry.resource.Recipes; import io.anuke.mindustry.resource.Upgrade; import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.ucore.modules.Module; @@ -25,23 +22,6 @@ public class NetCommon extends Module { weapon.shoot(player, packet.x, packet.y, packet.rotation); }); - Net.handle(PlacePacket.class, (packet) -> { - if(headless) - world.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation); - else - control.input().placeBlockInternal(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false); - - Recipe recipe = Recipes.getByResult(Block.getByID(packet.block)); - if (recipe != null) state.inventory.removeItems(recipe.requirements); - }); - - Net.handle(BreakPacket.class, (packet) -> { - if(headless) - world.removeBlock(world.tile(packet.x, packet.y)); - else - control.input().breakBlockInternal(packet.x, packet.y, false); - }); - Net.handle(ChatPacket.class, (packet) -> { ui.chatfrag.addMessage(packet.text, colorizeName(packet.id, packet.name)); }); diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index ef5e91e131..275985e110 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -7,14 +7,15 @@ import io.anuke.mindustry.entities.SyncEntity; import io.anuke.mindustry.game.EventType.GameOverEvent; import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.io.Version; +import io.anuke.mindustry.net.Administration; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.NetConnection; import io.anuke.mindustry.net.NetworkIO; import io.anuke.mindustry.net.Packets.*; -import io.anuke.mindustry.resource.Upgrade; -import io.anuke.mindustry.resource.UpgradeRecipes; -import io.anuke.mindustry.resource.Weapon; +import io.anuke.mindustry.resource.*; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Placement; import io.anuke.mindustry.world.Tile; import io.anuke.ucore.core.Events; import io.anuke.ucore.core.Timers; @@ -41,6 +42,8 @@ public class NetServer extends Module{ private final static int timerStateSync = 1; private final static int timerBlockSync = 2; + public final Administration admins = new Administration(); + /**Maps connection IDs to players.*/ private IntMap connections = new IntMap<>(); private ObjectMap weapons = new ObjectMap<>(); @@ -51,12 +54,26 @@ public class NetServer extends Module{ Events.on(GameOverEvent.class, () -> weapons.clear()); - Net.handleServer(Connect.class, (id, connect) -> {}); + Net.handleServer(Connect.class, (id, connect) -> { + if(admins.isBanned(connect.addressTCP)){ + Net.kickConnection(id, KickReason.banned); + } + }); Net.handleServer(ConnectPacket.class, (id, packet) -> { + if(Net.getConnection(id) == null || + admins.isBanned(Net.getConnection(id).address)) return; + + admins.setKnownName(Net.getConnection(id).address, packet.name); + + if(packet.version != Version.build && Version.build != -1){ + if(packet.version == -1){ + admins.banPlayer(Net.getConnection(id).address); + Net.kickConnection(id, KickReason.banned); + }else { + Net.kickConnection(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated); + } - if(packet.version != Version.build && packet.version != -1 && Version.build != -1){ //ignore 'custom builds' on both ends - Net.kickConnection(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated); return; } @@ -145,19 +162,41 @@ public class NetServer extends Module{ Net.handleServer(PlacePacket.class, (id, packet) -> { packet.playerid = connections.get(id).id; - Net.sendExcept(id, packet, SendMode.tcp); + + if(!Placement.validPlace(packet.x, packet.y, Block.getByID(packet.block))) return; + + Recipe recipe = Recipes.getByResult(Block.getByID(packet.block)); + + if(recipe == null) return; + + state.inventory.removeItems(recipe.requirements); + + Placement.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false); + + Net.send(packet, SendMode.tcp); }); Net.handleServer(BreakPacket.class, (id, packet) -> { packet.playerid = connections.get(id).id; - Net.sendExcept(id, packet, SendMode.tcp); + + if(!Placement.validBreak(packet.x, packet.y)) return; + + Placement.breakBlock(packet.x, packet.y, true, false); + + Net.send(packet, SendMode.tcp); }); Net.handleServer(ChatPacket.class, (id, packet) -> { + if(!Timers.get("chatFlood" + id, 20)){ + ChatPacket warn = new ChatPacket(); + warn.text = "[scarlet]You are sending messages too quickly."; + Net.sendTo(id, warn, SendMode.tcp); + return; + } Player player = connections.get(id); packet.name = player.name; packet.id = player.id; - Net.sendExcept(player.clientid, packet, SendMode.tcp); + Net.send(packet, SendMode.tcp); }); Net.handleServer(UpgradePacket.class, (id, packet) -> { diff --git a/core/src/io/anuke/mindustry/core/Renderer.java b/core/src/io/anuke/mindustry/core/Renderer.java index 99620f2aed..b2064b0588 100644 --- a/core/src/io/anuke/mindustry/core/Renderer.java +++ b/core/src/io/anuke/mindustry/core/Renderer.java @@ -274,6 +274,7 @@ public class Renderer extends RendererModule{ void drawEnemyMarkers(){ Graphics.surface(indicatorSurface); Draw.color(Color.RED); + for(Enemy enemy : enemyGroup.all()) { if (rect.setSize(camera.viewportWidth, camera.viewportHeight).setCenter(camera.position.x, camera.position.y) diff --git a/core/src/io/anuke/mindustry/core/ThreadHandler.java b/core/src/io/anuke/mindustry/core/ThreadHandler.java index b4f08fc703..c3823cc692 100644 --- a/core/src/io/anuke/mindustry/core/ThreadHandler.java +++ b/core/src/io/anuke/mindustry/core/ThreadHandler.java @@ -27,7 +27,10 @@ public class ThreadHandler { public ThreadHandler(ThreadProvider impl){ this.impl = impl; - Timers.setDeltaProvider(() -> impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime()*60f); + Timers.setDeltaProvider(() ->{ + float result = impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime()*60f; + return Float.isNaN(result) ? 1f : result; + }); } public void run(Runnable r){ diff --git a/core/src/io/anuke/mindustry/core/UI.java b/core/src/io/anuke/mindustry/core/UI.java index 85483571e3..89a6deff52 100644 --- a/core/src/io/anuke/mindustry/core/UI.java +++ b/core/src/io/anuke/mindustry/core/UI.java @@ -45,6 +45,8 @@ public class UI extends SceneModule{ public ControlsDialog controls; public MapEditorDialog editor; public LanguageDialog language; + public BansDialog bans; + public AdminsDialog admins; public final MenuFragment menufrag = new MenuFragment(); public final ToolFragment toolfrag = new ToolFragment(); @@ -150,6 +152,8 @@ public class UI extends SceneModule{ paused = new PausedDialog(); about = new AboutDialog(); host = new HostDialog(); + bans = new BansDialog(); + admins = new AdminsDialog(); build.begin(scene); diff --git a/core/src/io/anuke/mindustry/core/World.java b/core/src/io/anuke/mindustry/core/World.java index f9506a7b2a..ac75f0f0a4 100644 --- a/core/src/io/anuke/mindustry/core/World.java +++ b/core/src/io/anuke/mindustry/core/World.java @@ -8,10 +8,7 @@ import io.anuke.mindustry.ai.Pathfind; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.game.SpawnPoint; import io.anuke.mindustry.io.Maps; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Map; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.WorldGenerator; +import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.blocks.Blocks; import io.anuke.mindustry.world.blocks.DistributionBlocks; import io.anuke.mindustry.world.blocks.ProductionBlocks; @@ -22,7 +19,8 @@ import io.anuke.ucore.modules.Module; import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Tmp; -import static io.anuke.mindustry.Vars.*; +import static io.anuke.mindustry.Vars.control; +import static io.anuke.mindustry.Vars.tilesize; public class World extends Module{ private int seed; @@ -182,7 +180,7 @@ public class World extends Module{ core = WorldGenerator.generate(map.pixmap, tiles, spawns); - placeBlock(core.x, core.y, ProductionBlocks.core, 0); + Placement.placeBlock(core.x, core.y, ProductionBlocks.core, 0, false, false); if(!map.name.equals("tutorial")){ setDefaultBlocks(); @@ -233,7 +231,7 @@ public class World extends Module{ public int getSeed(){ return seed; } - + public void removeBlock(Tile tile){ if(!tile.block().isMultiblock() && !tile.isLinked()){ tile.setBlock(Blocks.air); @@ -246,32 +244,6 @@ public class World extends Module{ } } } - - public void placeBlock(int x, int y, Block result, int rotation){ - Tile tile = world.tile(x, y); - - //just in case - if(tile == null) return; - - tile.setBlock(result, rotation); - - if(result.isMultiblock()){ - int offsetx = -(result.width-1)/2; - int offsety = -(result.height-1)/2; - - for(int dx = 0; dx < result.width; dx ++){ - for(int dy = 0; dy < result.height; dy ++){ - int worldx = dx + offsetx + x; - int worldy = dy + offsety + y; - if(!(worldx == x && worldy == y)){ - Tile toplace = world.tile(worldx, worldy); - if(toplace != null) - toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety)); - } - } - } - } - } public TileEntity findTileTarget(float x, float y, Tile tile, float range, boolean damaged){ Entity closest = null; diff --git a/core/src/io/anuke/mindustry/entities/Player.java b/core/src/io/anuke/mindustry/entities/Player.java index 86a28df1eb..eef5a5094b 100644 --- a/core/src/io/anuke/mindustry/entities/Player.java +++ b/core/src/io/anuke/mindustry/entities/Player.java @@ -83,7 +83,7 @@ public class Player extends SyncEntity{ @Override public void onDeath(){ - remove(); + dead = true; if(Net.active()){ NetEvents.handlePlayerDeath(); } @@ -112,7 +112,7 @@ public class Player extends SyncEntity{ @Override public void drawSmooth(){ - if((debug && (!showPlayer || !showUI)) || (isAndroid && isLocal) || (dead && !isLocal)) return; + if((debug && (!showPlayer || !showUI)) || (isAndroid && isLocal) || dead) return; boolean snap = snapCamera && Settings.getBool("smoothcam") && Settings.getBool("pixelate") && isLocal; String part = isAndroid ? "ship" : "mech"; diff --git a/core/src/io/anuke/mindustry/input/InputHandler.java b/core/src/io/anuke/mindustry/input/InputHandler.java index 1aae96c7c1..3a10fba086 100644 --- a/core/src/io/anuke/mindustry/input/InputHandler.java +++ b/core/src/io/anuke/mindustry/input/InputHandler.java @@ -2,26 +2,16 @@ package io.anuke.mindustry.input; import com.badlogic.gdx.InputAdapter; import com.badlogic.gdx.math.GridPoint2; -import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.Array; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.game.SpawnPoint; -import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.NetEvents; import io.anuke.mindustry.resource.ItemStack; import io.anuke.mindustry.resource.Recipe; -import io.anuke.mindustry.resource.Recipes; import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Placement; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.mindustry.world.blocks.ProductionBlocks; -import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Graphics; -import io.anuke.ucore.core.Sounds; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.SolidEntity; import io.anuke.ucore.util.Mathf; import static io.anuke.mindustry.Vars.*; @@ -35,8 +25,6 @@ public abstract class InputHandler extends InputAdapter{ public PlaceMode lastPlaceMode = placeMode; public PlaceMode lastBreakMode = breakMode; - private Rectangle rect = new Rectangle(); - public abstract void update(); public abstract float getCursorX(); public abstract float getCursorY(); @@ -88,40 +76,6 @@ public abstract class InputHandler extends InputAdapter{ public boolean validPlace(int x, int y, Block type){ - for(int i = 0; i < world.getSpawns().size; i ++){ - SpawnPoint spawn = world.getSpawns().get(i); - if(Vector2.dst(x * tilesize, y * tilesize, spawn.start.worldx(), spawn.start.worldy()) < enemyspawnspace){ - return false; - } - } - - rect.setSize(type.width * tilesize, type.height * tilesize); - Vector2 offset = type.getPlaceOffset(); - rect.setCenter(offset.x + x * tilesize, offset.y + y * tilesize); - - synchronized (Entities.entityLock) { - for (SolidEntity e : Entities.getNearby(enemyGroup, x * tilesize, y * tilesize, tilesize * 2f)) { - if (e == null) continue; //not sure why this happens? - Rectangle rect = e.hitbox.getRect(e.x, e.y); - - if (this.rect.overlaps(rect)) { - return false; - } - } - } - - if(type.solid || type.solidifes) { - for (Player player : playerGroup.all()) { - if (!player.isAndroid && rect.overlaps(player.hitbox.getRect(player.x, player.y))) { - return false; - } - } - } - - Tile tile = world.tile(x, y); - - if(tile == null || (isSpawnPoint(tile) && (type.solidifes || type.solid))) return false; - if(!type.isMultiblock() && control.tutorial().active() && control.tutorial().showBlock()){ @@ -136,40 +90,11 @@ public abstract class InputHandler extends InputAdapter{ }else if(control.tutorial().active()){ return false; } - - if(type.isMultiblock()){ - int offsetx = -(type.width-1)/2; - int offsety = -(type.height-1)/2; - for(int dx = 0; dx < type.width; dx ++){ - for(int dy = 0; dy < type.height; dy ++){ - Tile other = world.tile(x + dx + offsetx, y + dy + offsety); - if(other == null || (other.block() != Blocks.air && !other.block().alwaysReplace) || isSpawnPoint(other)){ - return false; - } - } - } - return true; - }else{ - if(tile.block() != type && (type.canReplace(tile.block()) || tile.block().alwaysReplace) && tile.block().isMultiblock() == type.isMultiblock()){ - return true; - } - return tile.block() == Blocks.air; - } - } - public boolean isSpawnPoint(Tile tile){ - return tile != null && tile.x == world.getCore().x && tile.y == world.getCore().y - 2; + return Placement.validPlace(x, y, type); } public boolean validBreak(int x, int y){ - Tile tile = world.tile(x, y); - - if(tile == null || tile.block() == ProductionBlocks.core) return false; - - if(tile.isLinked() && tile.getLinked().block() == ProductionBlocks.core){ - return false; - } - if(control.tutorial().active()){ if(control.tutorial().showBlock()){ @@ -186,102 +111,25 @@ public abstract class InputHandler extends InputAdapter{ } } - return tile.breakable(); + return Placement.validBreak(x, y); } public void placeBlock(int x, int y, Block result, int rotation, boolean effects, boolean sound){ - - placeBlockInternal(x, y, result, rotation, effects, sound); + if(!Net.client()) Placement.placeBlock(x, y, result, rotation, effects, sound); Tile tile = world.tile(x, y); - if(tile != null) result.placed(tile); - if(Net.active() && result != ProductionBlocks.core){ + if(Net.active()){ NetEvents.handlePlace(x, y, result, rotation); } } - public void placeBlockInternal(int x, int y, Block result, int rotation, boolean effects, boolean sound){ - Tile tile = world.tile(x, y); - - //just in case - if(tile == null) - return; - - tile.setBlock(result, rotation); - - if(result.isMultiblock()){ - int offsetx = -(result.width-1)/2; - int offsety = -(result.height-1)/2; - - for(int dx = 0; dx < result.width; dx ++){ - for(int dy = 0; dy < result.height; dy ++){ - int worldx = dx + offsetx + x; - int worldy = dy + offsety + y; - if(!(worldx == x && worldy == y)){ - Tile toplace = world.tile(worldx, worldy); - if(toplace != null) - toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety)); - } - - if(effects) Effects.effect(Fx.place, worldx * tilesize, worldy * tilesize); - } - } - }else{ - if(effects) Effects.effect(Fx.place, x * tilesize, y * tilesize); - } - - if(effects && sound) Sounds.play("place"); - } - public void breakBlock(int x, int y, boolean sound){ - breakBlockInternal(x, y, sound); + if(!Net.client()) Placement.breakBlock(x, y, sound, sound); if(Net.active()){ NetEvents.handleBreak(x, y); } } - - public void breakBlockInternal(int x, int y, boolean sound){ - Tile tile = world.tile(x, y); - - if(tile == null) return; - - Block block = tile.isLinked() ? tile.getLinked().block() : tile.block(); - Recipe result = null; - - for(Recipe recipe : Recipes.all()){ - if(recipe.result == block){ - result = recipe; - break; - } - } - - if(result != null){ - for(ItemStack stack : result.requirements){ - state.inventory.addItem(stack.item, (int)(stack.amount * breakDropAmount)); - } - } - - if(tile.block().drops != null){ - state.inventory.addItem(tile.block().drops.item, tile.block().drops.amount); - } - - //Effects.shake(3f, 1f, player); - if(sound) Sounds.play("break"); - - if(!tile.block().isMultiblock() && !tile.isLinked()){ - tile.setBlock(Blocks.air); - Effects.effect(Fx.breakBlock, tile.worldx(), tile.worldy()); - }else{ - Tile target = tile.isLinked() ? tile.getLinked() : tile; - Array removals = target.getLinkedTiles(); - for(Tile toremove : removals){ - //note that setting a new block automatically unlinks it - toremove.setBlock(Blocks.air); - Effects.effect(Fx.breakBlock, toremove.worldx(), toremove.worldy()); - } - } - } } diff --git a/core/src/io/anuke/mindustry/io/Maps.java b/core/src/io/anuke/mindustry/io/Maps.java index 7d583a6174..038bc99d25 100644 --- a/core/src/io/anuke/mindustry/io/Maps.java +++ b/core/src/io/anuke/mindustry/io/Maps.java @@ -75,13 +75,14 @@ public class Maps implements Disposable{ } public void loadMaps(){ - if(!loadMapFile(Gdx.files.internal("maps/maps.json"))){ + if(!loadMapFile(Gdx.files.internal("maps/maps.json"), true)){ throw new RuntimeException("Failed to load maps!"); } if(!gwt) { - if (!loadMapFile(customMapDirectory.child("maps.json"))) { + if (!loadMapFile(customMapDirectory.child("maps.json"), false)) { try { + Log.info("Failed to find custom map directory. Creating one instead."); customMapDirectory.child("maps.json").writeString("{}", false); } catch (Exception e) { throw new RuntimeException("Failed to create custom map directory!"); @@ -159,7 +160,7 @@ public class Maps implements Disposable{ saveMaps(out, customMapDirectory.child("maps.json")); } - private boolean loadMapFile(FileHandle file){ + private boolean loadMapFile(FileHandle file, boolean logException){ try{ Array arr = json.fromJson(ArrayContainer.class, file).maps; if(arr != null){ //can be an empty map file @@ -176,8 +177,10 @@ public class Maps implements Disposable{ } return true; }catch(Exception e){ - Log.err(e); - Log.err("Failed loading map file: {0}", file); + if(logException) { + Log.err(e); + Log.err("Failed loading map file: {0}", file); + } return false; } } diff --git a/core/src/io/anuke/mindustry/net/Administration.java b/core/src/io/anuke/mindustry/net/Administration.java new file mode 100644 index 0000000000..82f9190098 --- /dev/null +++ b/core/src/io/anuke/mindustry/net/Administration.java @@ -0,0 +1,114 @@ +package io.anuke.mindustry.net; + +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Json; +import com.badlogic.gdx.utils.ObjectMap; +import io.anuke.ucore.core.Settings; + +public class Administration { + private Json json = new Json(); + private Array bannedIPS = new Array<>(); + private Array admins = new Array<>(); + private ObjectMap known = new ObjectMap<>(); + + public Administration(){ + Settings.defaultList( + "bans", "{}", + "admins", "{}", + "knownIPs", "{}" + ); + + load(); + } + + /**Sets last known name for an IP.*/ + public void setKnownName(String ip, String name){ + known.put(ip, name); + saveKnown(); + } + + /**Returns the last known name for an IP. Returns 'unknown' if this IP has an unknown username.*/ + public String getLastName(String ip){ + return known.get(ip, "unknown"); + } + + /**Returns list of banned IPs.*/ + public Array getBanned(){ + return bannedIPS; + } + + /**Bans a player by IP; returns whether this player was already banned.*/ + public boolean banPlayer(String ip){ + if(bannedIPS.contains(ip, false)) + return false; + bannedIPS.add(ip); + saveBans(); + + return true; + } + + /**Unbans a player by IP; returns whether this player was banned in the first place..*/ + public boolean unbanPlayer(String ip){ + if(!bannedIPS.contains(ip, false)) + return false; + bannedIPS.removeValue(ip, false); + saveBans(); + + return true; + } + + /**Returns list of banned IPs.*/ + public Array getAdmins(){ + return admins; + } + + /**Makes a player an admin. Returns whether this player was already an admin.*/ + public boolean adminPlayer(String ip){ + if(admins.contains(ip, false)) + return false; + admins.add(ip); + saveAdmins(); + + return true; + } + + /**Makes a player no longer an admin. Returns whether this player was an admin in the first place.*/ + public boolean unAdminPlayer(String ip){ + if(!admins.contains(ip, false)) + return false; + admins.removeValue(ip, false); + saveAdmins(); + + return true; + } + + public boolean isBanned(String ip){ + return bannedIPS.contains(ip, false); + } + + public boolean isAdmin(String ip){ + return admins.contains(ip, false); + } + + private void saveKnown(){ + Settings.putString("knownIPs", json.toJson(known)); + Settings.save(); + } + + private void saveBans(){ + Settings.putString("bans", json.toJson(bannedIPS)); + Settings.save(); + } + + private void saveAdmins(){ + Settings.putString("admins", json.toJson(admins)); + Settings.save(); + } + + private void load(){ + bannedIPS = json.fromJson(Array.class, Settings.getString("bans")); + admins = json.fromJson(Array.class, Settings.getString("admins")); + known = json.fromJson(ObjectMap.class, Settings.getString("knownIPs")); + } + +} diff --git a/core/src/io/anuke/mindustry/net/Host.java b/core/src/io/anuke/mindustry/net/Host.java index 751bde7982..415b90913f 100644 --- a/core/src/io/anuke/mindustry/net/Host.java +++ b/core/src/io/anuke/mindustry/net/Host.java @@ -3,11 +3,15 @@ package io.anuke.mindustry.net; public class Host { public final String name; public final String address; + public final String mapname; + public final int wave; public final int players; - public Host(String name, String address, int players){ + public Host(String name, String address, String mapname, int wave, int players){ this.name = name; this.address = address; this.players = players; + this.mapname = mapname; + this.wave = wave; } } diff --git a/core/src/io/anuke/mindustry/net/NetEvents.java b/core/src/io/anuke/mindustry/net/NetEvents.java index 5656da784b..332edae663 100644 --- a/core/src/io/anuke/mindustry/net/NetEvents.java +++ b/core/src/io/anuke/mindustry/net/NetEvents.java @@ -13,7 +13,6 @@ import io.anuke.mindustry.world.Tile; import io.anuke.ucore.entities.Entity; import static io.anuke.mindustry.Vars.netCommon; -import static io.anuke.mindustry.Vars.ui; public class NetEvents { @@ -99,8 +98,6 @@ public class NetEvents { packet.name = Vars.player.name; packet.id = Vars.player.id; Net.send(packet, SendMode.tcp); - - ui.chatfrag.addMessage(packet.text, netCommon.colorizeName(Vars.player.id, Vars.player.name)); } public static void handleShoot(Weapon weapon, float x, float y, float angle){ diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index 183fb3a8e9..fe676d4496 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -371,7 +371,7 @@ public class Packets { } public enum KickReason{ - kick, invalidPassword, clientOutdated, serverOutdated + kick, invalidPassword, clientOutdated, serverOutdated, banned } public static class UpgradePacket implements Packet{ @@ -558,4 +558,22 @@ public class Packets { itemid = buffer.get(); } } + + public static class NetErrorPacket implements Packet{ + public String message; + + @Override + public void write(ByteBuffer buffer) { + buffer.putShort((short)message.getBytes().length); + buffer.put(message.getBytes()); + } + + @Override + public void read(ByteBuffer buffer) { + short length = buffer.getShort(); + byte[] bytes = new byte[length]; + buffer.get(bytes); + message = new String(bytes); + } + } } diff --git a/core/src/io/anuke/mindustry/net/Registrator.java b/core/src/io/anuke/mindustry/net/Registrator.java index 929cee1910..05532c12e1 100644 --- a/core/src/io/anuke/mindustry/net/Registrator.java +++ b/core/src/io/anuke/mindustry/net/Registrator.java @@ -40,7 +40,8 @@ public class Registrator { EntitySpawnPacket.class, ItemTransferPacket.class, ItemSetPacket.class, - ItemOffloadPacket.class + ItemOffloadPacket.class, + NetErrorPacket.class, }; private static ObjectIntMap> ids = new ObjectIntMap<>(); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/AdminsDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/AdminsDialog.java new file mode 100644 index 0000000000..ebc1759904 --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/dialogs/AdminsDialog.java @@ -0,0 +1,53 @@ +package io.anuke.mindustry.ui.dialogs; + +import io.anuke.ucore.scene.ui.ScrollPane; +import io.anuke.ucore.scene.ui.layout.Table; + +import static io.anuke.mindustry.Vars.netServer; +import static io.anuke.mindustry.Vars.ui; + +public class AdminsDialog extends FloatingDialog { + + public AdminsDialog(){ + super("$text.server.admins"); + + addCloseButton(); + + setup(); + shown(this::setup); + } + + private void setup(){ + content().clear(); + + float w = 400f, h = 80f; + + Table table = new Table(); + + ScrollPane pane = new ScrollPane(table, "clear"); + pane.setFadeScrollBars(false); + + if(netServer.admins.getAdmins().size == 0){ + table.add("$text.server.admins.none"); + } + + for(String ip : netServer.admins.getAdmins()){ + Table res = new Table("button"); + res.margin(14f); + + res.labelWrap("[LIGHT_GRAY]" + netServer.admins.getLastName(ip)).width(w - h - 24f); + res.add().growX(); + res.addImageButton("icon-cancel", 14*3, () -> { + ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> { + netServer.admins.unAdminPlayer(ip); + setup(); + }); + }).size(h).pad(-14f); + + table.add(res).width(w).height(h); + table.row(); + } + + content().add(pane); + } +} diff --git a/core/src/io/anuke/mindustry/ui/dialogs/BansDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/BansDialog.java new file mode 100644 index 0000000000..8191d7d81c --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/dialogs/BansDialog.java @@ -0,0 +1,53 @@ +package io.anuke.mindustry.ui.dialogs; + +import io.anuke.ucore.scene.ui.ScrollPane; +import io.anuke.ucore.scene.ui.layout.Table; + +import static io.anuke.mindustry.Vars.*; + +public class BansDialog extends FloatingDialog { + + public BansDialog(){ + super("$text.server.bans"); + + addCloseButton(); + + setup(); + + shown(this::setup); + } + + private void setup(){ + content().clear(); + + float w = 400f, h = 80f; + + Table table = new Table(); + + ScrollPane pane = new ScrollPane(table, "clear"); + pane.setFadeScrollBars(false); + + if(netServer.admins.getBanned().size == 0){ + table.add("$text.server.bans.none"); + } + + for(String ip : netServer.admins.getBanned()){ + Table res = new Table("button"); + res.margin(14f); + + res.labelWrap("IP: [LIGHT_GRAY]" + ip + "\n[]Name: [LIGHT_GRAY]" + netServer.admins.getLastName(ip)).width(w - h - 24f); + res.add().growX(); + res.addImageButton("icon-cancel", 14*3, () -> { + ui.showConfirm("$text.confirm", "$text.confirmunban", () -> { + netServer.admins.unbanPlayer(ip); + setup(); + }); + }).size(h).pad(-14f); + + table.add(res).width(w).height(h); + table.row(); + } + + content().add(pane); + } +} diff --git a/core/src/io/anuke/mindustry/ui/dialogs/HostDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/HostDialog.java index d4991027bf..0c7878e8bf 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/HostDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/HostDialog.java @@ -31,13 +31,13 @@ public class HostDialog extends FloatingDialog{ Settings.put("name", text); Settings.save(); ui.listfrag.rebuild(); - }).grow().pad(8); + }).grow().pad(8).get().setMaxLength(48); ImageButton button = t.addImageButton("white", 40, () -> { new ColorPickDialog().show(color -> { player.color.set(color); Settings.putInt("color", Color.rgba8888(color)); - Settings.save();; + Settings.save(); }); }).size(50f, 54f).get(); button.update(() -> button.getStyle().imageUpColor = player.getColor()); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java index 8a5234ee0d..19a579ab9a 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java @@ -85,7 +85,7 @@ public class JoinDialog extends FloatingDialog { TextButton button = buttons[0] = remote.addButton("[accent]"+server.ip, "clear", () -> { if(!buttons[0].childrenPressed()) connect(server.ip, Vars.port); - }).width(w).height(120f).pad(4f).get(); + }).width(w).height(140f).pad(4f).get(); button.getLabel().setWrap(true); @@ -134,10 +134,14 @@ public class JoinDialog extends FloatingDialog { Net.pingHost(server.ip, server.port, host -> { server.content.clear(); - server.content.add("[lightgray]" + Bundles.format("text.server.hostname", host.name)).pad(4); + server.content.add("[lightgray]" + Bundles.format("text.server.hostname", host.name)).left(); server.content.row(); server.content.add("[lightgray]" + (host.players != 1 ? Bundles.format("text.players", host.players) : - Bundles.format("text.players.single", host.players))); + Bundles.format("text.players.single", host.players))).left(); + server.content.row(); + server.content.add("[lightgray]" + Bundles.format("text.save.map", host.mapname)).left(); + server.content.row(); + server.content.add("[lightgray]" + Bundles.format("text.save.wave", host.wave)).left(); }, e -> { server.content.clear(); server.content.add("$text.host.invalid"); @@ -175,7 +179,7 @@ public class JoinDialog extends FloatingDialog { Vars.player.name = text; Settings.put("name", text); Settings.save(); - }).grow().pad(8); + }).grow().pad(8).get().setMaxLength(48); ImageButton button = t.addImageButton("white", 40, () -> { new ColorPickDialog().show(color -> { diff --git a/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java b/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java index 0933cee7b7..76a63a06bc 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java @@ -3,12 +3,14 @@ package io.anuke.mindustry.ui.fragments; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.net.NetConnection; import io.anuke.mindustry.net.NetEvents; import io.anuke.mindustry.net.Packets.KickReason; import io.anuke.mindustry.ui.BorderImage; import io.anuke.ucore.core.Inputs; import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.scene.Element; +import io.anuke.ucore.scene.builders.button; import io.anuke.ucore.scene.builders.label; import io.anuke.ucore.scene.builders.table; import io.anuke.ucore.scene.ui.ScrollPane; @@ -40,10 +42,20 @@ public class PlayerListFragment implements Fragment{ row(); new table("pane"){{ margin(12f); + get().addCheck("$text.server.friendlyfire", b -> { state.friendlyFire = b; NetEvents.handleFriendlyFireChange(b); - }).growX().update(i -> i.setChecked(state.friendlyFire)).disabled(b -> Net.client()); + }).growX().update(i -> i.setChecked(state.friendlyFire)).disabled(b -> Net.client()).padRight(5); + + new button("$text.server.bans", () -> { + ui.bans.show(); + }).padTop(-12).padBottom(-12).fillY().cell.disabled(b -> Net.client()); + + new button("$text.server.admins", () -> { + ui.admins.show(); + }).padTop(-12).padBottom(-12).padRight(-12).fillY().cell.disabled(b -> Net.client()); + }}.pad(10f).growX().end(); }}.end(); @@ -69,9 +81,13 @@ public class PlayerListFragment implements Fragment{ public void rebuild(){ content.clear(); - float h = 60f; + float h = 74f; for(Player player : playerGroup.all()){ + NetConnection connection = Net.getConnection(player.clientid); + + if(connection == null && !player.isLocal) continue; + Table button = new Table("button"); button.left(); button.margin(5).marginBottom(10); @@ -93,19 +109,50 @@ public class PlayerListFragment implements Fragment{ } }); } - button.add(stack).size(h); - button.add("[#" + player.getColor().toString().toUpperCase() + "]" + player.name).pad(10); + button.labelWrap("[#" + player.getColor().toString().toUpperCase() + "]" + player.name).width(170f).pad(10); button.add().grow(); if(Net.server() && !player.isLocal){ button.add().growY(); - button.addImageButton("icon-cancel", 14*3, () -> - Net.kickConnection(player.clientid, KickReason.kick) - ).pad(-5).padBottom(-10).size(h+10, h+14); + + float bs = (h + 14)/2f; + + button.table(t -> { + t.defaults().size(bs - 1, bs + 3); + + t.addImageButton("icon-ban", 14*2, () -> { + ui.showConfirm("$text.confirm", "$text.confirmban", () -> { + netServer.admins.banPlayer(connection.address); + Net.kickConnection(player.clientid, KickReason.banned); + }); + }).padBottom(-5.1f); + + t.addImageButton("icon-cancel", 14*2, () -> Net.kickConnection(player.clientid, KickReason.kick)).padBottom(-5.1f); + + t.row(); + + t.addImageButton("icon-admin", "toggle", 14*2, () -> { + if(netServer.admins.isAdmin(connection.address)){ + ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> { + netServer.admins.unAdminPlayer(connection.address); + //TODO send aproppriate packets. + }); + }else{ + ui.showConfirm("$text.confirm", "$text.confirmadmin", () -> { + netServer.admins.adminPlayer(connection.address); + //TODO send aproppriate packets. + }); + } + }).update(b -> b.setChecked(netServer.admins.isAdmin(connection.address))); + + t.addImageButton("icon-cancel", 14*2, () -> Net.kickConnection(player.clientid, KickReason.kick)); + }).padRight(12).padTop(-5).padLeft(0).padBottom(-10).size(bs + 10f, bs); + + } - content.add(button).padBottom(-5).width(350f); + content.add(button).padBottom(-6).width(350f).maxHeight(h + 14); content.row(); } diff --git a/core/src/io/anuke/mindustry/world/Placement.java b/core/src/io/anuke/mindustry/world/Placement.java new file mode 100644 index 0000000000..e37c3e25b3 --- /dev/null +++ b/core/src/io/anuke/mindustry/world/Placement.java @@ -0,0 +1,164 @@ +package io.anuke.mindustry.world; + +import com.badlogic.gdx.math.Rectangle; +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.utils.Array; +import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.game.SpawnPoint; +import io.anuke.mindustry.graphics.Fx; +import io.anuke.mindustry.resource.ItemStack; +import io.anuke.mindustry.resource.Recipe; +import io.anuke.mindustry.resource.Recipes; +import io.anuke.mindustry.world.blocks.Blocks; +import io.anuke.mindustry.world.blocks.ProductionBlocks; +import io.anuke.ucore.core.Effects; +import io.anuke.ucore.core.Sounds; +import io.anuke.ucore.entities.Entities; +import io.anuke.ucore.entities.SolidEntity; + +import static io.anuke.mindustry.Vars.*; + +public class Placement { + private static final Rectangle rect = new Rectangle(); + + public static void breakBlock(int x, int y, boolean effect, boolean sound){ + Tile tile = world.tile(x, y); + + if(tile == null) return; + + Block block = tile.isLinked() ? tile.getLinked().block() : tile.block(); + Recipe result = null; + + for(Recipe recipe : Recipes.all()){ + if(recipe.result == block){ + result = recipe; + break; + } + } + + if(result != null){ + for(ItemStack stack : result.requirements){ + state.inventory.addItem(stack.item, (int)(stack.amount * breakDropAmount)); + } + } + + if(tile.block().drops != null){ + state.inventory.addItem(tile.block().drops.item, tile.block().drops.amount); + } + + if(sound) Sounds.play("break"); + + if(!tile.block().isMultiblock() && !tile.isLinked()){ + tile.setBlock(Blocks.air); + if(effect) Effects.effect(Fx.breakBlock, tile.worldx(), tile.worldy()); + }else{ + Tile target = tile.isLinked() ? tile.getLinked() : tile; + Array removals = target.getLinkedTiles(); + for(Tile toremove : removals){ + //note that setting a new block automatically unlinks it + toremove.setBlock(Blocks.air); + if(effect) Effects.effect(Fx.breakBlock, toremove.worldx(), toremove.worldy()); + } + } + } + + public static void placeBlock(int x, int y, Block result, int rotation, boolean effects, boolean sound){ + Tile tile = world.tile(x, y); + + //just in case + if(tile == null) return; + + tile.setBlock(result, rotation); + + if(result.isMultiblock()){ + int offsetx = -(result.width-1)/2; + int offsety = -(result.height-1)/2; + + for(int dx = 0; dx < result.width; dx ++){ + for(int dy = 0; dy < result.height; dy ++){ + int worldx = dx + offsetx + x; + int worldy = dy + offsety + y; + if(!(worldx == x && worldy == y)){ + Tile toplace = world.tile(worldx, worldy); + if(toplace != null) + toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety)); + } + + if(effects) Effects.effect(Fx.place, worldx * tilesize, worldy * tilesize); + } + } + }else if(effects) Effects.effect(Fx.place, x * tilesize, y * tilesize); + + if(effects && sound) Sounds.play("place"); + } + + public static boolean validPlace(int x, int y, Block type){ + for(int i = 0; i < world.getSpawns().size; i ++){ + SpawnPoint spawn = world.getSpawns().get(i); + if(Vector2.dst(x * tilesize, y * tilesize, spawn.start.worldx(), spawn.start.worldy()) < enemyspawnspace){ + return false; + } + } + + rect.setSize(type.width * tilesize, type.height * tilesize); + Vector2 offset = type.getPlaceOffset(); + rect.setCenter(offset.x + x * tilesize, offset.y + y * tilesize); + + synchronized (Entities.entityLock) { + for (SolidEntity e : Entities.getNearby(enemyGroup, x * tilesize, y * tilesize, tilesize * 2f)) { + if (e == null) continue; //not sure why this happens? + Rectangle rect = e.hitbox.getRect(e.x, e.y); + + if (Placement.rect.overlaps(rect)) { + return false; + } + } + } + + if(type.solid || type.solidifes) { + for (Player player : playerGroup.all()) { + if (!player.isAndroid && rect.overlaps(player.hitbox.getRect(player.x, player.y))) { + return false; + } + } + } + + Tile tile = world.tile(x, y); + + if(tile == null || (isSpawnPoint(tile) && (type.solidifes || type.solid))) return false; + + if(type.isMultiblock()){ + int offsetx = -(type.width-1)/2; + int offsety = -(type.height-1)/2; + for(int dx = 0; dx < type.width; dx ++){ + for(int dy = 0; dy < type.height; dy ++){ + Tile other = world.tile(x + dx + offsetx, y + dy + offsety); + if(other == null || (other.block() != Blocks.air && !other.block().alwaysReplace) || isSpawnPoint(other)){ + return false; + } + } + } + return true; + }else { + return tile.block() != type + && (type.canReplace(tile.block()) || tile.block().alwaysReplace) + && tile.block().isMultiblock() == type.isMultiblock() || tile.block() == Blocks.air; + } + } + + public static boolean isSpawnPoint(Tile tile){ + return tile != null && tile.x == world.getCore().x && tile.y == world.getCore().y - 2; + } + + public static boolean validBreak(int x, int y){ + Tile tile = world.tile(x, y); + + if(tile == null || tile.block() == ProductionBlocks.core) return false; + + if(tile.isLinked() && tile.getLinked().block() == ProductionBlocks.core){ + return false; + } + + return tile.breakable(); + } +} diff --git a/html/src/io/anuke/mindustry/client/WebsocketClient.java b/html/src/io/anuke/mindustry/client/WebsocketClient.java index 7118e45c5f..66b82fe32a 100644 --- a/html/src/io/anuke/mindustry/client/WebsocketClient.java +++ b/html/src/io/anuke/mindustry/client/WebsocketClient.java @@ -103,7 +103,7 @@ public class WebsocketClient implements ClientProvider { } @Override - public void pingHost(String address, int port, Consumer valid, Consumer failed) { + public void pingHost(String address, int port, Consumer valid, Consumer failed) { if(!Platform.instance.canJoinGame()) { failed.accept(new IOException()); }else { @@ -119,7 +119,7 @@ public class WebsocketClient implements ClientProvider { public void onMessage(String msg) { if(!msg.startsWith("---")) return; String[] text = msg.substring(3).split("\\|"); - Host host = new Host(text[1], address, Strings.parseInt(text[0])); + Host host = new Host(text[1], address, text[2], Strings.parseInt(text[3]), Strings.parseInt(text[0])); valid.accept(host); accepted[0] = true; socket.close(); diff --git a/kryonet/src/io/anuke/kryonet/KryoClient.java b/kryonet/src/io/anuke/kryonet/KryoClient.java index 4a946541f3..77cf87be44 100644 --- a/kryonet/src/io/anuke/kryonet/KryoClient.java +++ b/kryonet/src/io/anuke/kryonet/KryoClient.java @@ -35,7 +35,7 @@ public class KryoClient implements ClientProvider{ handler = new ClientDiscoveryHandler() { @Override public DatagramPacket onRequestNewDatagramPacket() { - return new DatagramPacket(new byte[32], 32); + return new DatagramPacket(new byte[128], 128); } @Override diff --git a/kryonet/src/io/anuke/kryonet/KryoRegistrator.java b/kryonet/src/io/anuke/kryonet/KryoRegistrator.java index a30486cbce..007580013b 100644 --- a/kryonet/src/io/anuke/kryonet/KryoRegistrator.java +++ b/kryonet/src/io/anuke/kryonet/KryoRegistrator.java @@ -2,7 +2,6 @@ package io.anuke.kryonet; import com.esotericsoftware.minlog.Log; import com.esotericsoftware.minlog.Log.Logger; -import io.anuke.mindustry.Vars; import io.anuke.mindustry.net.Host; import io.anuke.ucore.util.ColorCodes; @@ -11,8 +10,7 @@ import java.io.StringWriter; import java.net.InetAddress; import java.nio.ByteBuffer; -import static io.anuke.mindustry.Vars.headless; -import static io.anuke.mindustry.Vars.playerGroup; +import static io.anuke.mindustry.Vars.*; public class KryoRegistrator { public static boolean fakeLag = false; @@ -49,24 +47,44 @@ public class KryoRegistrator { } public static ByteBuffer writeServerData(){ - String host = headless ? "Server" : Vars.player.name; + int maxlen = 32; + + String host = (headless ? "Server" : player.name); + String map = world.getMap().name; + + host = host.substring(0, Math.min(host.length(), maxlen)); + map = map.substring(0, Math.min(map.length(), maxlen)); + + ByteBuffer buffer = ByteBuffer.allocate(128); - ByteBuffer buffer = ByteBuffer.allocate(1 + host.getBytes().length + 4); buffer.put((byte)host.getBytes().length); buffer.put(host.getBytes()); + + buffer.put((byte)map.getBytes().length); + buffer.put(map.getBytes()); + buffer.putInt(playerGroup.size()); + buffer.putInt(state.wave); return buffer; } public static Host readServerData(InetAddress ia, ByteBuffer buffer){ - //old version address. - if(buffer.capacity() == 4) return null; + if(buffer.capacity() < 128) return null; //old version address. + + byte hlength = buffer.get(); + byte[] hb = new byte[hlength]; + buffer.get(hb); + + byte mlength = buffer.get(); + byte[] mb = new byte[mlength]; + buffer.get(mb); + + String host = new String(hb); + String map = new String(mb); - byte length = buffer.get(); - byte[] sname = new byte[length]; - buffer.get(sname); int players = buffer.getInt(); + int wave = buffer.getInt(); - return new Host(new String(sname), ia.getHostAddress(), players); + return new Host(host, ia.getHostAddress(), map, wave, players); } } diff --git a/kryonet/src/io/anuke/kryonet/KryoServer.java b/kryonet/src/io/anuke/kryonet/KryoServer.java index 7e8cacfeb4..4b65ac1586 100644 --- a/kryonet/src/io/anuke/kryonet/KryoServer.java +++ b/kryonet/src/io/anuke/kryonet/KryoServer.java @@ -14,10 +14,7 @@ import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Net.ServerProvider; import io.anuke.mindustry.net.NetConnection; -import io.anuke.mindustry.net.Packets.Connect; -import io.anuke.mindustry.net.Packets.Disconnect; -import io.anuke.mindustry.net.Packets.KickPacket; -import io.anuke.mindustry.net.Packets.KickReason; +import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Registrator; import io.anuke.mindustry.net.Streamable; import io.anuke.mindustry.net.Streamable.StreamBegin; @@ -25,6 +22,7 @@ import io.anuke.mindustry.net.Streamable.StreamChunk; import io.anuke.ucore.UCore; import io.anuke.ucore.core.Timers; import io.anuke.ucore.util.Log; +import io.anuke.ucore.util.Strings; import org.java_websocket.WebSocket; import org.java_websocket.exceptions.WebsocketNotConnectedException; import org.java_websocket.handshake.ClientHandshake; @@ -66,13 +64,15 @@ public class KryoServer implements ServerProvider { @Override public void connected (Connection connection) { - KryoConnection kn = new KryoConnection(lastconnection ++, connection.getRemoteAddressTCP().toString(), connection); + String ip = connection.getRemoteAddressTCP().getAddress().getHostAddress(); + + KryoConnection kn = new KryoConnection(lastconnection ++, ip, connection); Connect c = new Connect(); c.id = kn.id; - c.addressTCP = connection.getRemoteAddressTCP().toString(); + c.addressTCP = ip; - Log.info("&bRecieved connection: {0} {1}", c.id, c.addressTCP); + Log.info("&bRecieved connection: {0} / {1}", c.id, c.addressTCP); connections.add(kn); Gdx.app.postRunnable(() -> Net.handleServerReceived(kn.id, c)); @@ -141,12 +141,15 @@ public class KryoServer implements ServerProvider { if(con == null){ Log.err("Cannot kick unknown player!"); return; + }else{ + Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", connection, con.address, reason); } KickPacket p = new KickPacket(); p.reason = reason; con.send(p, SendMode.tcp); + Timers.runTask(2f, con::close); } @Override @@ -371,9 +374,21 @@ public class KryoServer implements ServerProvider { connection.sendUDP(object); } }catch (Exception e){ - e.printStackTrace(); + Log.err(e); Log.info("Disconnecting invalid client!"); + try{ + NetErrorPacket packet = new NetErrorPacket(); + packet.message = Strings.parseException(e, true); + Timers.runTask(5f, connection::close); + }catch (Exception e2){ + Log.err(e2); + connection.close(); + } connection.close(); + + KryoConnection k = getByKryoID(connection.getID()); + if(k != null) connections.remove(k); + Log.info("Connection removed {0}", k); } } } @@ -389,6 +404,8 @@ public class KryoServer implements ServerProvider { } + + class SocketServer extends WebSocketServer { public SocketServer(int port) { diff --git a/mindustry-maps/maps.json b/mindustry-maps/maps.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/mindustry-maps/maps.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 6c54e11028..9003cccc85 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -38,7 +38,12 @@ public class ServerControl extends Module { private ShuffleMode mode; public ServerControl(){ - Settings.defaults("shufflemode", "normal"); + Settings.defaultList( + "shufflemode", "normal", + "bans", "", + "admins", "" + ); + mode = ShuffleMode.valueOf(Settings.getString("shufflemode")); Effects.setScreenShakeProvider((a, b) -> {}); @@ -184,6 +189,21 @@ public class ServerControl extends Module { } }); + handler.register("players", "Display player info.", arg -> { + if(state.is(State.menu)){ + info("&lyServer is closed."); + }else{ + if(playerGroup.size() > 0) { + info("&lyPlayers: {0}", playerGroup.size()); + for (Player p : playerGroup.all()) { + print(" &y{0} / Connection {1} / IP: {2}", p.name, p.clientid, Net.getConnection(p.clientid).address); + } + }else{ + info("&lyNo players connected."); + } + } + }); + handler.register("say", "", "Send a message to all players.", arg -> { if(!state.is(State.playing)) { err("Not hosting. Host a game first."); @@ -237,11 +257,6 @@ public class ServerControl extends Module { return; } - if(playerGroup.size() == 0){ - err("But this server is empty. A barren wasteland."); - return; - } - Player target = null; for(Player player : playerGroup.all()){ @@ -259,6 +274,130 @@ public class ServerControl extends Module { } }); + handler.register("ban", "", "Ban a person by name.", arg -> { + if(!state.is(State.playing)) { + err("Can't ban people by name with no players."); + return; + } + + Player target = null; + + for(Player player : playerGroup.all()){ + if(player.name.equalsIgnoreCase(arg[0])){ + target = player; + break; + } + } + + if(target != null){ + String ip = Net.getConnection(player.clientid).address; + netServer.admins.banPlayer(ip); + Net.kickConnection(target.clientid, KickReason.banned); + info("Banned player by IP: {0}", ip); + }else{ + info("Nobody with that name could be found."); + } + }); + + handler.register("bans", "List all banned IPs.", arg -> { + Array bans = netServer.admins.getBanned(); + + if(bans.size == 0){ + Log.info("No banned players have been found."); + }else{ + Log.info("&lyBanned players:"); + for(String string : bans){ + Log.info(" &luy {0} / Last known name: '{1}'", string, netServer.admins.getLastName(string)); + } + } + }); + + handler.register("banip", "", "Ban a person by IP.", arg -> { + if(netServer.admins.banPlayer(arg[0])) { + info("Banned player by IP: {0}.", arg[0]); + + for(Player player : playerGroup.all()){ + if(Net.getConnection(player.clientid).address.equals(arg[0])){ + Net.kickConnection(player.clientid, KickReason.banned); + break; + } + } + }else{ + err("That IP is already banned!"); + } + }); + + handler.register("unbanip", "", "Unban a person by IP.", arg -> { + if(netServer.admins.unbanPlayer(arg[0])) { + info("Unbanned player by IP: {0}.", arg[0]); + }else{ + err("That IP is not banned!"); + } + }); + + //assadsad + + handler.register("admin", "", "Make a user admin", arg -> { + if(!state.is(State.playing)) { + err("Open the server first."); + return; + } + + Player target = null; + + for(Player player : playerGroup.all()){ + if(player.name.equalsIgnoreCase(arg[0])){ + target = player; + break; + } + } + + if(target != null){ + String ip = Net.getConnection(player.clientid).address; + netServer.admins.adminPlayer(ip); + info("Admin-ed player by IP: {0} / {1}", ip, arg[0]); + }else{ + info("Nobody with that name could be found."); + } + }); + + handler.register("unadmin", "", "Removes admin status from a player", arg -> { + if(!state.is(State.playing)) { + err("Open the server first."); + return; + } + + Player target = null; + + for(Player player : playerGroup.all()){ + if(player.name.equalsIgnoreCase(arg[0])){ + target = player; + break; + } + } + + if(target != null){ + String ip = Net.getConnection(player.clientid).address; + netServer.admins.unAdminPlayer(ip); + info("Un-admin-ed player by IP: {0} / {1}", ip, arg[0]); + }else{ + info("Nobody with that name could be found."); + } + }); + + handler.register("admins", "List all banned IPs.", arg -> { + Array admins = netServer.admins.getAdmins(); + + if(admins.size == 0){ + Log.info("No admins have been found."); + }else{ + Log.info("&lyAdmins:"); + for(String string : admins){ + Log.info(" &luy {0} / Name: '{1}'", string, netServer.admins.getLastName(string)); + } + } + }); + handler.register("runwave", "Trigger the next wave.", arg -> { if(!state.is(State.playing)) { err("Not hosting. Host a game first.");