Multithreading cleanup

This commit is contained in:
Anuken
2018-10-10 23:43:48 -04:00
parent 67db5e9dfc
commit 52bd079c0a
18 changed files with 50 additions and 359 deletions

View File

@@ -111,7 +111,6 @@ public class WaveSpawner{
if(group.type.isFlying){
FlyerSpawn spawn = flySpawns.get(flyCount);
//TODO verify flyer spawn
float margin = 40f; //how far away from the edge flying units spawn
spawnX = world.width() * tilesize / 2f + Mathf.sqrwavex(spawn.angle) * (world.width() / 2f * tilesize + margin);

View File

@@ -176,7 +176,7 @@ public class ContentLoader{
}
public void dispose(){
//TODO clear all content.
//clear all content, currently not needed
}
public void handleContent(Content content){

View File

@@ -27,6 +27,7 @@ import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.EntityQuery;
import io.anuke.ucore.modules.Module;
import io.anuke.ucore.util.Atlas;
import io.anuke.ucore.util.Timer;
import static io.anuke.mindustry.Vars.*;
@@ -43,6 +44,7 @@ public class Control extends Module{
public final Saves saves;
public final Unlocks unlocks;
private Timer timerRPC= new Timer(), timerUnlock = new Timer();
private boolean hiscore = false;
private boolean wasPaused = false;
private InputHandler[] inputs = {};
@@ -348,12 +350,12 @@ public class Control extends Module{
}
//auto-update rpc every 5 seconds
if(Timers.get("rpcUpdate", 60 * 5)){
if(timerRPC.get(60 * 5)){
Platform.instance.updateRPC();
}
//check unlocks every 2 seconds
if(!state.mode.infiniteResources && Timers.get("timerCheckUnlock", 120)){
if(!state.mode.infiniteResources && timerUnlock.get(120)){
checkUnlockableBlocks();
//save if the unlocks changed

View File

@@ -117,7 +117,7 @@ public class UI extends SceneModule{
}
@Override
public synchronized void update(){
public void update(){
if(Graphics.drawing()) Graphics.end();
act();

View File

@@ -54,14 +54,17 @@ public class Units{
return invalidateTarget(target, targeter.team, targeter.x, targeter.y, targeter.getWeapon().getAmmo().getRange());
}
/**
* Returns whether there are any entities on this tile.
*/
/**Returns whether there are any entities on this tile.*/
public static boolean anyEntities(Tile tile){
Block type = tile.block();
rect.setSize(type.size * tilesize, type.size * tilesize);
rect.setCenter(tile.drawx(), tile.drawy());
return anyEntities(rect);
}
public static boolean anyEntities(Rectangle rect){
boolResult = false;
Units.getNearby(rect, unit -> {
@@ -78,9 +81,7 @@ public class Units{
return boolResult;
}
/**
* Returns whether there are any entities on this tile, with the hitbox expanded.
*/
/**Returns whether there are any entities on this tile, with the hitbox expanded.*/
public static boolean anyEntities(Tile tile, float expansion, Predicate<Unit> pred){
Block type = tile.block();
rect.setSize(type.size * tilesize + expansion, type.size * tilesize + expansion);

View File

@@ -10,7 +10,7 @@ import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Mathf;
/**
* Class that renders a trail.
* Class that renders a colored trail.
*/
public class Trail{
private final static float maxJump = 15f;

View File

@@ -330,8 +330,11 @@ public abstract class InputHandler extends InputAdapter{
public boolean validPlace(int x, int y, Block type, int rotation){
for(Tile tile : state.teams.get(player.getTeam()).cores){
if(tile.distanceTo(x * tilesize, y * tilesize) < coreBuildRange){
return Build.validPlace(player.getTeam(), x, y, type, rotation) &&
Vector2.dst(player.x, player.y, x * tilesize, y * tilesize) < Player.placeDistance;
//TODO terrible hack
try{
return Build.validPlace(player.getTeam(), x, y, type, rotation) &&
Vector2.dst(player.x, player.y, x * tilesize, y * tilesize) < Player.placeDistance;
}catch(Exception e){return false;}
}
}
@@ -343,12 +346,10 @@ public abstract class InputHandler extends InputAdapter{
}
public void placeBlock(int x, int y, Recipe recipe, int rotation){
//todo multiplayer support
player.addBuildRequest(new BuildRequest(x, y, rotation, recipe));
}
public void breakBlock(int x, int y){
//todo multiplayer support
Tile tile = world.tile(x, y).target();
player.addBuildRequest(new BuildRequest(tile.x, tile.y));
}

View File

@@ -28,7 +28,6 @@ import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.*;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.scene.Group;
@@ -84,19 +83,17 @@ public class MobileInput extends InputHandler implements GestureListener{
/** Check and assign targets for a specific position. */
void checkTargets(float x, float y){
synchronized(Entities.entityLock){
Unit unit = Units.getClosestEnemy(player.getTeam(), x, y, 20f, u -> true);
Unit unit = Units.getClosestEnemy(player.getTeam(), x, y, 20f, u -> true);
if(unit != null){
threads.run(() -> player.target = unit);
}else{
Tile tile = world.tileWorld(x, y);
if(tile != null) tile = tile.target();
if(unit != null){
threads.run(() -> player.target = unit);
}else{
Tile tile = world.tileWorld(x, y);
if(tile != null) tile = tile.target();
if(tile != null && state.teams.areEnemies(player.getTeam(), tile.getTeam())){
TileEntity entity = tile.entity;
threads.run(() -> player.target = entity);
}
if(tile != null && state.teams.areEnemies(player.getTeam(), tile.getTeam())){
TileEntity entity = tile.entity;
threads.run(() -> player.target = entity);
}
}
}
@@ -550,7 +547,7 @@ public class MobileInput extends InputHandler implements GestureListener{
//ignore off-screen taps
if(cursor == null || ui.hasMouse(x, y)) return false;
checkTargets(worldx, worldy);
threads.run(() -> checkTargets(worldx, worldy));
//remove if request present
if(hasRequest(cursor)){

View File

@@ -1,267 +0,0 @@
package io.anuke.mindustry.maps.generation.pathfinding;
import com.badlogic.gdx.math.GridPoint2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.BinaryHeap;
import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.function.Predicate;
import io.anuke.ucore.util.Structs;
import io.anuke.ucore.util.Geometry;
//TODO
public class AStarPathFinder extends TilePathfinder{
NodeRecord[] records;
BinaryHeap<NodeRecord> openList;
NodeRecord current;
Predicate<Tile> goal;
private int searchId;
private Tile end;
private static final byte UNVISITED = 0;
private static final byte OPEN = 1;
private static final byte CLOSED = 2;
private static final boolean debug = false;
public AStarPathFinder(Tile[][] tiles) {
super(tiles);
this.records = new NodeRecord[tiles.length * tiles[0].length];
this.openList = new BinaryHeap<>();
}
@Override
public void search(Tile start, Tile end, Array<Tile> out){
}
public void search(Tile start, Tile end, Predicate<Tile> pred, Array<Tile> out){
this.goal = pred;
searchNodePath(start, end, out);
}
public boolean searchNodePath(Tile startNode, Tile endNode, Array<Tile> outPath) {
this.end = endNode;
// Perform AStar
boolean found = search(startNode, endNode);
if (found) {
// Create a path made of nodes
generateNodePath(startNode, outPath);
}
return found;
}
protected boolean search(Tile startNode, Tile endNode) {
initSearch(startNode, endNode);
// Iterate through processing each node
do {
// Retrieve the node with smallest estimated total cost from the open list
current = openList.pop();
current.category = CLOSED;
// Terminate if we reached the goal node
if (current.node == endNode || goal.test(current.node)) return true;
visitChildren(endNode);
} while (openList.size > 0);
// We've run out of nodes without finding the goal, so there's no solution
return false;
}
/*
public boolean search(PathFinderRequest<Tile> request, long timeToRun) {
long lastTime = TimeUtils.nanoTime();
// We have to initialize the search if the status has just changed
if (request.statusChanged) {
initSearch(request.startNode, request.endNode);
request.statusChanged = false;
}
// Iterate through processing each node
do {
// Check the available time
long currentTime = TimeUtils.nanoTime();
timeToRun -= currentTime - lastTime;
if (timeToRun <= PathFinderQueue.TIME_TOLERANCE) return false;
// Retrieve the node with smallest estimated total cost from the open list
current = openList.pop();
current.category = CLOSED;
// Terminate if we reached the goal node; we've found a path.
if (current.node == request.endNode) {
request.pathFound = true;
generateNodePath(request.startNode, request.resultPath);
return true;
}
// Visit current node's children
visitChildren(request.endNode);
// Store the current time
lastTime = currentTime;
} while (openList.size > 0);
// The open list is empty and we've not found a path.
request.pathFound = false;
return true;
}*/
protected void initSearch(Tile startNode, Tile endNode) {
// Increment the search id
if (++searchId < 0) searchId = 1;
// Initialize the open list
openList.clear();
// Initialize the record for the start node and add it to the open list
NodeRecord startRecord = getNodeRecord(startNode);
startRecord.node = startNode;
startRecord.from = null;
startRecord.costSoFar = 0;
addToOpenList(startRecord, estimate(startNode, endNode));
current = null;
}
protected void visitChildren(Tile endNode) {
if(debug) Effects.effect(Fx.spawn, current.node.worldx(), current.node.worldy());
nodes(current.node, node -> {
float addCost = estimate(current.node, node);
float nodeCost = current.costSoFar + addCost;
float nodeHeuristic;
NodeRecord nodeRecord = getNodeRecord(node);
if (nodeRecord.category == CLOSED) { // The node is closed
// If we didn't find a shorter route, skip
if (nodeRecord.costSoFar <= nodeCost){
return;
}
// We can use the node's old cost values to calculate its heuristic
// without calling the possibly expensive heuristic function
nodeHeuristic = nodeRecord.getEstimatedTotalCost() - nodeRecord.costSoFar;
} else if (nodeRecord.category == OPEN) { // The node is open
//If our route is no better, then skip
if (nodeRecord.costSoFar <= nodeCost){
return;
}
// Remove it from the open list (it will be re-added with the new cost)
openList.remove(nodeRecord);
// We can use the node's old cost values to calculate its heuristic
// without calling the possibly expensive heuristic function
nodeHeuristic = nodeRecord.getEstimatedTotalCost() - nodeRecord.costSoFar;
} else { // the node is unvisited
// We'll need to calculate the heuristic value using the function,
// since we don't have a node record with a previously calculated value
nodeHeuristic = estimate(node, endNode);
}
// Update node record's cost and connection
nodeRecord.costSoFar = nodeCost;
nodeRecord.from = current.node;
// Add it to the open list with the estimated total cost
addToOpenList(nodeRecord, nodeCost + nodeHeuristic);
});
}
protected void nodes(Tile current, Consumer<Tile> cons){
if(obstacle(current)) return;
for(GridPoint2 p : Geometry.d4){
int wx = current.x + p.x, wy = current.y + p.y;
Tile n = Structs.inBounds(wx, wy, tiles) ? tiles[wx][wy] : null;
if(!obstacle(n)) cons.accept(n);
}
}
protected boolean obstacle(Tile tile){
return tile == null || (tile.solid() && end.target() != tile && tile.target() != end) || !tile.block().alwaysReplace;
}
protected float estimate(Tile tile, Tile other){
return goal.test(other) || goal.test(tile) ? Float.MIN_VALUE : Math.abs(tile.worldx() - other.worldx()) + Math.abs(tile.worldy() - other.worldy());
}
protected void generateNodePath(Tile startNode, Array<Tile> outPath) {
// Work back along the path, accumulating nodes
outPath.clear();
while (current.from != null) {
outPath.add(current.node);
current = records[(indexOf(current.from))];
}
outPath.add(startNode);
// Reverse the path
outPath.reverse();
}
protected void addToOpenList(NodeRecord nodeRecord, float estimatedTotalCost) {
openList.add(nodeRecord, estimatedTotalCost);
nodeRecord.category = OPEN;
}
protected NodeRecord getNodeRecord(Tile node) {
if(records[indexOf(node)] == null){
NodeRecord record = new NodeRecord();
record.node = node;
record.searchId = searchId;
records[indexOf(node)] = record;
return record;
}else{
NodeRecord record = records[indexOf(node)];
if(record.searchId != searchId){
record.category = UNVISITED;
record.searchId = searchId;
}
return record;
}
}
private int indexOf(Tile node){
return node.packedPosition();
}
static class NodeRecord extends BinaryHeap.Node {
Tile node;
Tile from;
float costSoFar;
byte category;
int searchId;
public NodeRecord() {
super(0);
}
public float getEstimatedTotalCost() {
return getValue();
}
}
}

View File

@@ -26,8 +26,6 @@ import static io.anuke.mindustry.Vars.headless;
import static io.anuke.mindustry.Vars.ui;
public class Net{
public static final Object packetPoolLock = new Object();
private static boolean server;
private static boolean active;
private static boolean clientLoaded;
@@ -239,16 +237,12 @@ public class Net{
if(clientLoaded || ((object instanceof Packet) && ((Packet) object).isImportant())){
if(clientListeners.get(object.getClass()) != null)
clientListeners.get(object.getClass()).accept(object);
synchronized(packetPoolLock){
Pooling.free(object);
}
Pooling.free(object);
}else if(!((object instanceof Packet) && ((Packet) object).isUnimportant())){
packetQueue.add(object);
Log.info("Queuing packet {0}.", object);
}else{
synchronized(packetPoolLock){
Pooling.free(object);
}
Pooling.free(object);
}
}else{
Log.err("Unhandled packet type: '{0}'!", object);
@@ -263,9 +257,7 @@ public class Net{
if(serverListeners.get(object.getClass()) != null){
if(serverListeners.get(object.getClass()) != null)
serverListeners.get(object.getClass()).accept(connection, object);
synchronized(packetPoolLock){
Pooling.free(object);
}
Pooling.free(object);
}else{
Log.err("Unhandled packet type: '{0}'!", object.getClass());
}

View File

@@ -153,8 +153,6 @@ public class NetworkIO{
Player player = players[0];
//TODO !! use map name as the network map in Maps, so getMap() isn't null.
try(DataInputStream stream = new DataInputStream(is)){
float timerTime = stream.readFloat();
long timestamp = stream.readLong();

View File

@@ -17,12 +17,14 @@ import io.anuke.ucore.scene.ui.Image;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Timer;
import static io.anuke.mindustry.Vars.*;
public class PlayerListFragment extends Fragment{
private boolean visible = false;
private Table content = new Table().marginRight(13f).marginLeft(13f);
private Timer timer = new Timer();
@Override
public void build(Group parent){
@@ -34,7 +36,7 @@ public class PlayerListFragment extends Fragment{
return;
}
if(visible && Timers.get("player-list-rebuild", 20)){
if(visible && timer.get(20)){
rebuild();
}
});
@@ -42,8 +44,7 @@ public class PlayerListFragment extends Fragment{
cont.table("pane", pane -> {
pane.label(() -> Bundles.format(playerGroup.size() == 1 ? "text.players.single" : "text.players", playerGroup.size()));
pane.row();
pane.pane("clear", content)
.grow().get().setScrollingDisabled(true, false);
pane.pane("clear", content).grow().get().setScrollingDisabled(true, false);
pane.row();
pane.table("pane", menu -> {

View File

@@ -11,7 +11,6 @@ import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
import io.anuke.ucore.core.Events;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.util.Geometry;
import static io.anuke.mindustry.Vars.*;
@@ -113,30 +112,9 @@ public class Build{
return false;
}
rect.setSize(type.size * tilesize, type.size * tilesize);
rect.setCenter(type.offset() + x * tilesize, type.offset() + y * tilesize);
if(type.solid || type.solidifes){
synchronized(Entities.entityLock){
try{
rect.setSize(tilesize * type.size).setCenter(x * tilesize + type.offset(), y * tilesize + type.offset());
boolean[] result = {false};
Units.getNearby(rect, e -> {
if(e == null) return; //not sure why this happens?
e.getHitbox(hitrect);
if(rect.overlaps(hitrect) && !e.isFlying()){
result[0] = true;
}
});
if(result[0]) return false;
}catch(Exception e){
return false;
}
}
if((type.solid || type.solidifes) &&
Units.anyEntities(rect.setSize(tilesize * type.size).setCenter(x * tilesize + type.offset(), y * tilesize + type.offset()))){
return false;
}
//check for enemy cores

View File

@@ -64,19 +64,15 @@ public class Edges{
return polygons[(int) (radius * 2) - 1];
}
public static synchronized GridPoint2[] getEdges(int size){
public static GridPoint2[] getEdges(int size){
if(size < 0 || size > maxSize) throw new RuntimeException("Block size must be between 0 and " + maxSize);
return edges[size - 1];
}
public static synchronized GridPoint2[] getInsideEdges(int size){
public static GridPoint2[] getInsideEdges(int size){
if(size < 0 || size > maxSize) throw new RuntimeException("Block size must be between 0 and " + maxSize);
return edgeInside[size - 1];
}
public static synchronized int getEdgeAmount(int size){
return getEdges(size).length;
}
}

View File

@@ -198,7 +198,7 @@ public class Conveyor extends Block{
}
@Override
public synchronized void update(Tile tile){
public void update(Tile tile){
ConveyorEntity entity = tile.entity();
entity.minitem = 1f;
@@ -274,7 +274,7 @@ public class Conveyor extends Block{
}
@Override
public synchronized int removeStack(Tile tile, Item item, int amount){
public int removeStack(Tile tile, Item item, int amount){
ConveyorEntity entity = tile.entity();
entity.noSleep();
int removed = 0;
@@ -300,13 +300,13 @@ public class Conveyor extends Block{
}
@Override
public synchronized int acceptStack(Item item, int amount, Tile tile, Unit source){
public int acceptStack(Item item, int amount, Tile tile, Unit source){
ConveyorEntity entity = tile.entity();
return Math.min((int)(entity.minitem / itemSpace), amount);
}
@Override
public synchronized void handleStack(Item item, int amount, Tile tile, Unit source){
public void handleStack(Item item, int amount, Tile tile, Unit source){
ConveyorEntity entity = tile.entity();
for(int i = amount - 1; i >= 0; i--){