Merge branch 'remove-multithreading' of https://github.com/Anuken/Mindustry

This commit is contained in:
Anuken
2018-11-12 11:00:09 -05:00
15 changed files with 240 additions and 446 deletions

View File

@@ -172,7 +172,6 @@ public class Logic extends Module{
@Override @Override
public void update(){ public void update(){
if(threads.isEnabled() && !threads.isOnThread()) return;
if(Vars.control != null){ if(Vars.control != null){
control.runUpdateLogic(); control.runUpdateLogic();
@@ -238,9 +237,5 @@ public class Logic extends Module{
checkGameOver(); checkGameOver();
} }
} }
if(threads.isEnabled()){
netServer.update();
}
} }
} }

View File

@@ -399,11 +399,11 @@ public class NetClient extends Module{
quiet = true; quiet = true;
} }
public synchronized void addRemovedEntity(int id){ public void addRemovedEntity(int id){
removed.add(id); removed.add(id);
} }
public synchronized boolean isEntityUsed(int id){ public boolean isEntityUsed(int id){
return removed.contains(id); return removed.contains(id);
} }
@@ -414,12 +414,10 @@ public class NetClient extends Module{
BuildRequest[] requests; BuildRequest[] requests;
synchronized(player.getPlaceQueue()){
requests = new BuildRequest[player.getPlaceQueue().size]; requests = new BuildRequest[player.getPlaceQueue().size];
for(int i = 0; i < requests.length; i++){ for(int i = 0; i < requests.length; i++){
requests[i] = player.getPlaceQueue().get(i); requests[i] = player.getPlaceQueue().get(i);
} }
}
Call.onClientShapshot(lastSent++, TimeUtils.millis(), player.x, player.y, Call.onClientShapshot(lastSent++, TimeUtils.millis(), player.x, player.y,
player.pointerX, player.pointerY, player.rotation, player.baseRotation, player.pointerX, player.pointerY, player.rotation, player.baseRotation,

View File

@@ -416,7 +416,6 @@ public class NetServer extends Module{
} }
public void update(){ public void update(){
if(threads.isEnabled() && !threads.isOnThread()) return;
if(!headless && !closing && Net.server() && state.is(State.menu)){ if(!headless && !closing && Net.server() && state.is(State.menu)){
closing = true; closing = true;

View File

@@ -1,80 +1,35 @@
package io.anuke.mindustry.core; package io.anuke.mindustry.core;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.Queue;
import com.badlogic.gdx.utils.TimeUtils; import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Log;
import io.anuke.ucore.util.Threads;
import io.anuke.ucore.util.Threads.ThreadInfoProvider; import io.anuke.ucore.util.Threads.ThreadInfoProvider;
import static io.anuke.mindustry.Vars.control;
import static io.anuke.mindustry.Vars.logic;
public class ThreadHandler implements ThreadInfoProvider{ public class ThreadHandler implements ThreadInfoProvider{
private final Queue<Runnable> toRun = new Queue<>();
private Thread thread, graphicsThread;
private final Object updateLock = new Object();
private float delta = 1f;
private float smoothDelta = 1f;
private long frame = 0, lastDeltaUpdate;
private float framesSinceUpdate;
private boolean enabled;
private boolean rendered = true;
private long lastFrameTime; private long lastFrameTime;
public ThreadHandler(){ public ThreadHandler(){
Threads.setThreadInfoProvider(this);
graphicsThread = Thread.currentThread();
Timers.setDeltaProvider(() -> { Timers.setDeltaProvider(() -> {
float result = isOnThread() ? delta : Gdx.graphics.getDeltaTime() * 60f; float result = Gdx.graphics.getDeltaTime() * 60f;
return Math.min(Float.isNaN(result) ? 1f : result, 15f); return Math.min(Float.isNaN(result) || Float.isInfinite(result) ? 1f : result, 15f);
}); });
} }
public void run(Runnable r){ public void run(Runnable r){
if(enabled){
synchronized(toRun){
toRun.addLast(r);
}
}else{
r.run(); r.run();
} }
}
public void runGraphics(Runnable r){ public void runGraphics(Runnable r){
if(enabled){
Gdx.app.postRunnable(r);
}else{
r.run(); r.run();
} }
}
public void runDelay(Runnable r){ public void runDelay(Runnable r){
if(enabled){
synchronized(toRun){
toRun.addLast(r);
}
}else{
Gdx.app.postRunnable(r); Gdx.app.postRunnable(r);
} }
}
public int getTPS(){
if(smoothDelta == 0f){
return 60;
}
return (int) (60 / smoothDelta);
}
public long getFrameID(){ public long getFrameID(){
return enabled ? frame : Gdx.graphics.getFrameId(); return Gdx.graphics.getFrameId();
}
public float getFramesSinceUpdate(){
return framesSinceUpdate;
} }
public void handleBeginRender(){ public void handleBeginRender(){
@@ -95,119 +50,16 @@ public class ThreadHandler implements ThreadInfoProvider{
} }
} }
} }
if(!enabled) return;
framesSinceUpdate += Timers.delta();
synchronized(updateLock){
rendered = true;
updateLock.notify();
}
}
public boolean isEnabled(){
return enabled;
}
public void setEnabled(boolean enabled){
if(enabled){
logic.doUpdate = false;
Timers.runTask(2f, () -> {
if(thread != null){
thread.interrupt();
thread = null;
}
thread = new Thread(this::runLogic);
thread.setDaemon(true);
thread.setName("Update Thread");
thread.start();
Log.info("Starting logic thread.");
this.enabled = true;
});
}else{
this.enabled = false;
if(thread != null){
thread.interrupt();
thread = null;
}
Timers.runTask(2f, () -> {
logic.doUpdate = true;
});
}
}
public boolean doInterpolate(){
return enabled && Gdx.graphics.getFramesPerSecond() - getTPS() > 20 && getTPS() < 30;
}
public boolean isOnThread(){
return Thread.currentThread() == thread;
} }
@Override @Override
public boolean isOnLogicThread() { public boolean isOnLogicThread() {
return !enabled || Thread.currentThread() == thread; return true;
} }
@Override @Override
public boolean isOnGraphicsThread() { public boolean isOnGraphicsThread() {
return !enabled || Thread.currentThread() == graphicsThread; return true;
} }
private void runLogic(){
try{
while(true){
long time = TimeUtils.nanoTime();
while(true){
Runnable r;
synchronized(toRun){
if(toRun.size > 0){
r = toRun.removeFirst();
}else{
break;
}
}
r.run();
}
logic.doUpdate = true;
logic.update();
logic.doUpdate = false;
long elapsed = TimeUtils.nanosToMillis(TimeUtils.timeSinceNanos(time));
long target = (long) ((1000) / 60f);
if(elapsed < target){
Thread.sleep(target - elapsed);
}
synchronized(updateLock){
while(!rendered){
updateLock.wait();
}
rendered = false;
}
long actuallyElapsed = TimeUtils.nanosToMillis(TimeUtils.timeSinceNanos(time));
delta = Math.max(actuallyElapsed, target) / 1000f * 60f;
if(TimeUtils.timeSinceMillis(lastDeltaUpdate) > 1000){
lastDeltaUpdate = TimeUtils.millis();
smoothDelta = delta;
}
frame++;
framesSinceUpdate = 0;
}
}catch(InterruptedException ex){
Log.info("Stopping logic thread.");
}catch(Throwable ex){
control.setError(ex);
}
}
} }

View File

@@ -70,7 +70,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
public TargetTrait moveTarget; public TargetTrait moveTarget;
private float walktime; private float walktime;
private Queue<BuildRequest> placeQueue = new ThreadQueue<>(); private Queue<BuildRequest> placeQueue = new Queue<>();
private Tile mining; private Tile mining;
private CarriableTrait carrying; private CarriableTrait carrying;
private Trail trail = new Trail(12); private Trail trail = new Trail(12);
@@ -421,7 +421,6 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
/**Draw all current build requests. Does not draw the beam effect, only the positions.*/ /**Draw all current build requests. Does not draw the beam effect, only the positions.*/
public void drawBuildRequests(){ public void drawBuildRequests(){
synchronized(getPlaceQueue()){
for(BuildRequest request : getPlaceQueue()){ for(BuildRequest request : getPlaceQueue()){
if(getCurrentRequest() == request) continue; if(getCurrentRequest() == request) continue;
@@ -470,7 +469,6 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
Draw.reset(); Draw.reset();
} }
}
//endregion //endregion

View File

@@ -259,7 +259,6 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
@Override @Override
public void update(){ public void update(){
synchronized(Tile.tileSetLock){
//TODO better smoke effect, this one is awful //TODO better smoke effect, this one is awful
if(health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) && if(health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) &&
Mathf.chance(0.009f * Timers.delta() * (1f - health / tile.block().health))){ Mathf.chance(0.009f * Timers.delta() * (1f - health / tile.block().health))){
@@ -281,7 +280,6 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
cons.update(this); cons.update(this);
} }
} }
}
@Override @Override
public EntityGroup targetGroup(){ public EntityGroup targetGroup(){

View File

@@ -84,7 +84,6 @@ public interface BuilderTrait extends Entity{
} }
default void readBuilding(DataInput input, boolean applyChanges) throws IOException{ default void readBuilding(DataInput input, boolean applyChanges) throws IOException{
synchronized(getPlaceQueue()){
if(applyChanges) getPlaceQueue().clear(); if(applyChanges) getPlaceQueue().clear();
byte type = input.readByte(); byte type = input.readByte();
@@ -110,7 +109,6 @@ public interface BuilderTrait extends Entity{
} }
} }
} }
}
/**Return whether this builder's place queue contains items.*/ /**Return whether this builder's place queue contains items.*/
default boolean isBuilding(){ default boolean isBuilding(){
@@ -122,7 +120,6 @@ public interface BuilderTrait extends Entity{
* Otherwise, a new place request is added to the queue. * Otherwise, a new place request is added to the queue.
*/ */
default void replaceBuilding(int x, int y, int rotation, Recipe recipe){ default void replaceBuilding(int x, int y, int rotation, Recipe recipe){
synchronized(getPlaceQueue()){
for(BuildRequest request : getPlaceQueue()){ for(BuildRequest request : getPlaceQueue()){
if(request.x == x && request.y == y){ if(request.x == x && request.y == y){
clearBuilding(); clearBuilding();
@@ -130,7 +127,6 @@ public interface BuilderTrait extends Entity{
return; return;
} }
} }
}
addBuildRequest(new BuildRequest(x, y, rotation, recipe)); addBuildRequest(new BuildRequest(x, y, rotation, recipe));
} }
@@ -142,7 +138,6 @@ public interface BuilderTrait extends Entity{
/**Add another build requests to the tail of the queue, if it doesn't exist there yet.*/ /**Add another build requests to the tail of the queue, if it doesn't exist there yet.*/
default void addBuildRequest(BuildRequest place){ default void addBuildRequest(BuildRequest place){
synchronized(getPlaceQueue()){
for(BuildRequest request : getPlaceQueue()){ for(BuildRequest request : getPlaceQueue()){
if(request.x == place.x && request.y == place.y){ if(request.x == place.x && request.y == place.y){
return; return;
@@ -154,17 +149,14 @@ public interface BuilderTrait extends Entity{
} }
getPlaceQueue().addLast(place); getPlaceQueue().addLast(place);
} }
}
/** /**
* Return the build requests currently active, or the one at the top of the queue. * Return the build requests currently active, or the one at the top of the queue.
* May return null. * May return null.
*/ */
default BuildRequest getCurrentRequest(){ default BuildRequest getCurrentRequest(){
synchronized(getPlaceQueue()){
return getPlaceQueue().size == 0 ? null : getPlaceQueue().first(); return getPlaceQueue().size == 0 ? null : getPlaceQueue().first();
} }
}
/** /**
* Update building mechanism for this unit. * Update building mechanism for this unit.
@@ -275,8 +267,6 @@ public interface BuilderTrait extends Entity{
/**Draw placement effects for an entity. This includes mining*/ /**Draw placement effects for an entity. This includes mining*/
default void drawBuilding(Unit unit){ default void drawBuilding(Unit unit){
BuildRequest request; BuildRequest request;
synchronized(getPlaceQueue()){
if(!isBuilding()){ if(!isBuilding()){
if(getMineTile() != null){ if(getMineTile() != null){
drawMining(unit); drawMining(unit);
@@ -285,7 +275,6 @@ public interface BuilderTrait extends Entity{
} }
request = getCurrentRequest(); request = getCurrentRequest();
}
Tile tile = world.tile(request.x, request.y); Tile tile = world.tile(request.x, request.y);

View File

@@ -40,12 +40,11 @@ import static io.anuke.mindustry.Vars.unitGroups;
import static io.anuke.mindustry.Vars.world; import static io.anuke.mindustry.Vars.world;
public class Drone extends FlyingUnit implements BuilderTrait{ public class Drone extends FlyingUnit implements BuilderTrait{
protected static float discoverRange = 120f;
protected static int timerRepairEffect = timerIndex++; protected static int timerRepairEffect = timerIndex++;
protected Item targetItem; protected Item targetItem;
protected Tile mineTile; protected Tile mineTile;
protected Queue<BuildRequest> placeQueue = new ThreadQueue<>(); protected Queue<BuildRequest> placeQueue = new Queue<>();
protected boolean isBreaking; protected boolean isBreaking;
public final UnitState public final UnitState
@@ -250,7 +249,6 @@ public class Drone extends FlyingUnit implements BuilderTrait{
for(BaseUnit unit : group.all()){ for(BaseUnit unit : group.all()){
if(unit instanceof Drone){ if(unit instanceof Drone){
Drone drone = (Drone)unit; Drone drone = (Drone)unit;
synchronized(drone.getPlaceQueue()){
if(drone.isBuilding()){ if(drone.isBuilding()){
//stop building if opposite building begins. //stop building if opposite building begins.
BuildRequest req = drone.getCurrentRequest(); BuildRequest req = drone.getCurrentRequest();
@@ -259,7 +257,6 @@ public class Drone extends FlyingUnit implements BuilderTrait{
drone.setState(drone.repair); drone.setState(drone.repair);
} }
} }
}
drone.notifyPlaced(entity, event.breaking); drone.notifyPlaced(entity, event.breaking);
} }

View File

@@ -106,8 +106,6 @@ public class BlockRenderer{
for(int x = minx; x <= maxx; x++){ for(int x = minx; x <= maxx; x++){
for(int y = miny; y <= maxy; y++){ for(int y = miny; y <= maxy; y++){
boolean expanded = (Math.abs(x - avgx) > rangex || Math.abs(y - avgy) > rangey); boolean expanded = (Math.abs(x - avgx) > rangex || Math.abs(y - avgy) > rangey);
synchronized(Tile.tileSetLock){
Tile tile = world.rawTile(x, y); Tile tile = world.rawTile(x, y);
if(tile != null){ if(tile != null){
@@ -137,7 +135,6 @@ public class BlockRenderer{
} }
} }
} }
}
Graphics.surface(); Graphics.surface();
Graphics.end(); Graphics.end();
@@ -171,7 +168,6 @@ public class BlockRenderer{
layerBegins(req.layer); layerBegins(req.layer);
} }
synchronized(Tile.tileSetLock){
Block block = req.tile.block(); Block block = req.tile.block();
if(req.layer == Layer.block){ if(req.layer == Layer.block){
@@ -181,7 +177,6 @@ public class BlockRenderer{
}else if(req.layer == block.layer2){ }else if(req.layer == block.layer2){
block.drawLayer2(req.tile); block.drawLayer2(req.tile);
} }
}
lastLayer = req.layer; lastLayer = req.layer;
} }
@@ -199,7 +194,6 @@ public class BlockRenderer{
BlockRequest req = requests.get(index); BlockRequest req = requests.get(index);
if(req.tile.getTeam() != team) continue; if(req.tile.getTeam() != team) continue;
synchronized(Tile.tileSetLock){
Block block = req.tile.block(); Block block = req.tile.block();
if(req.layer == Layer.block){ if(req.layer == Layer.block){
@@ -209,7 +203,7 @@ public class BlockRenderer{
}else if(req.layer == block.layer2){ }else if(req.layer == block.layer2){
block.drawLayer2(req.tile); block.drawLayer2(req.tile);
} }
}
} }
} }

View File

@@ -74,7 +74,6 @@ public class MinimapRenderer implements Disposable{
dx = Mathf.clamp(dx, sz, world.width() - sz); dx = Mathf.clamp(dx, sz, world.width() - sz);
dy = Mathf.clamp(dy, sz, world.height() - sz); dy = Mathf.clamp(dy, sz, world.height() - sz);
synchronized(units){
rect.set((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize); rect.set((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize);
Graphics.beginClip(x, y, w, h); Graphics.beginClip(x, y, w, h);
@@ -88,7 +87,6 @@ public class MinimapRenderer implements Disposable{
Graphics.endClip(); Graphics.endClip();
} }
}
public TextureRegion getRegion(){ public TextureRegion getRegion(){
if(texture == null) return null; if(texture == null) return null;
@@ -128,12 +126,10 @@ public class MinimapRenderer implements Disposable{
dx = Mathf.clamp(dx, sz, world.width() - sz); dx = Mathf.clamp(dx, sz, world.width() - sz);
dy = Mathf.clamp(dy, sz, world.height() - sz); dy = Mathf.clamp(dy, sz, world.height() - sz);
synchronized(units){
rect.set((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize); rect.set((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize);
units.clear(); units.clear();
Units.getNearby(rect, units::add); Units.getNearby(rect, units::add);
} }
}
private int colorFor(Tile tile){ private int colorFor(Tile tile){
tile = tile.target(); tile = tile.target();

View File

@@ -53,11 +53,8 @@ public class OverlayRenderer{
//draw config selected block //draw config selected block
if(input.frag.config.isShown()){ if(input.frag.config.isShown()){
Tile tile = input.frag.config.getSelectedTile(); Tile tile = input.frag.config.getSelectedTile();
synchronized(Tile.tileSetLock){
tile.block().drawConfigure(tile); tile.block().drawConfigure(tile);
} }
}
input.drawTop(); input.drawTop();
@@ -113,7 +110,6 @@ public class OverlayRenderer{
Draw.reset(); Draw.reset();
} }
synchronized(Tile.tileSetLock){
Block block = target.block(); Block block = target.block();
TileEntity entity = target.entity; TileEntity entity = target.entity;
@@ -159,7 +155,7 @@ public class OverlayRenderer{
target.block().drawSelect(target); target.block().drawSelect(target);
}
} }
} }

View File

@@ -22,7 +22,7 @@ public class Trail{
this.length = length; this.length = length;
} }
public synchronized void update(float curx, float cury){ public void update(float curx, float cury){
if(Vector2.dst(curx, cury, lastX, lastY) >= maxJump){ if(Vector2.dst(curx, cury, lastX, lastY) >= maxJump){
points.clear(); points.clear();
} }
@@ -39,11 +39,11 @@ public class Trail{
lastY = cury; lastY = cury;
} }
public synchronized void clear(){ public void clear(){
points.clear(); points.clear();
} }
public synchronized void draw(Color color, float stroke){ public void draw(Color color, float stroke){
Draw.color(color); Draw.color(color);
for(int i = 0; i < points.size - 2; i += 2){ for(int i = 0; i < points.size - 2; i += 2){

View File

@@ -190,11 +190,6 @@ public class SettingsMenuDialog extends SettingsDialog{
}); });
graphics.sliderPref("fpscap", 125, 5, 125, 5, s -> (s > 120 ? Bundles.get("setting.fpscap.none") : Bundles.format("setting.fpscap.text", s))); graphics.sliderPref("fpscap", 125, 5, 125, 5, s -> (s > 120 ? Bundles.get("setting.fpscap.none") : Bundles.format("setting.fpscap.text", s)));
graphics.checkPref("multithread", mobile, threads::setEnabled);
if(Settings.getBool("multithread")){
threads.setEnabled(true);
}
if(!mobile){ if(!mobile){
graphics.checkPref("vsync", true, b -> Gdx.graphics.setVSync(b)); graphics.checkPref("vsync", true, b -> Gdx.graphics.setVSync(b));

View File

@@ -126,7 +126,6 @@ public class HudFragment extends Fragment{
IntFormat tps = new IntFormat("text.tps"); IntFormat tps = new IntFormat("text.tps");
IntFormat ping = new IntFormat("text.ping"); IntFormat ping = new IntFormat("text.ping");
t.label(() -> fps.get(Gdx.graphics.getFramesPerSecond())).padRight(10); t.label(() -> fps.get(Gdx.graphics.getFramesPerSecond())).padRight(10);
t.label(() -> tps.get(threads.getTPS())).visible(() -> threads.isEnabled());
t.row(); t.row();
if(Net.hasClient()){ if(Net.hasClient()){
t.label(() -> ping.get(Net.getPing())).visible(Net::client).colspan(2); t.label(() -> ping.get(Net.getPing())).visible(Net::client).colspan(2);

View File

@@ -22,7 +22,6 @@ import static io.anuke.mindustry.Vars.*;
public class Tile implements PosTrait, TargetTrait{ public class Tile implements PosTrait, TargetTrait{
public static final Object tileSetLock = new Object();
/** /**
* The coordinates of the core tile this is linked to, in the form of two bytes packed into one. * The coordinates of the core tile this is linked to, in the form of two bytes packed into one.
* This is relative to the block it is linked to; negate coords to find the link. * This is relative to the block it is linked to; negate coords to find the link.
@@ -147,7 +146,6 @@ public class Tile implements PosTrait, TargetTrait{
} }
public void setBlock(Block type, int rotation){ public void setBlock(Block type, int rotation){
synchronized(tileSetLock){
preChanged(); preChanged();
if(rotation < 0) rotation = (-rotation + 2); if(rotation < 0) rotation = (-rotation + 2);
this.wall = type; this.wall = type;
@@ -155,26 +153,21 @@ public class Tile implements PosTrait, TargetTrait{
setRotation((byte) (rotation % 4)); setRotation((byte) (rotation % 4));
changed(); changed();
} }
}
public void setBlock(Block type, Team team){ public void setBlock(Block type, Team team){
synchronized(tileSetLock){
preChanged(); preChanged();
this.wall = type; this.wall = type;
this.team = (byte)team.ordinal(); this.team = (byte)team.ordinal();
this.link = 0; this.link = 0;
changed(); changed();
} }
}
public void setBlock(Block type){ public void setBlock(Block type){
synchronized(tileSetLock){
preChanged(); preChanged();
this.wall = type; this.wall = type;
this.link = 0; this.link = 0;
changed(); changed();
} }
}
public void setFloor(Floor type){ public void setFloor(Floor type){
this.floor = type; this.floor = type;
@@ -270,7 +263,7 @@ public class Tile implements PosTrait, TargetTrait{
* Returns the list of all tiles linked to this multiblock, or an empty array if it's not a multiblock. * Returns the list of all tiles linked to this multiblock, or an empty array if it's not a multiblock.
* This array contains all linked tiles, including this tile itself. * This array contains all linked tiles, including this tile itself.
*/ */
public synchronized Array<Tile> getLinkedTiles(Array<Tile> tmpArray){ public Array<Tile> getLinkedTiles(Array<Tile> tmpArray){
Block block = block(); Block block = block();
tmpArray.clear(); tmpArray.clear();
if(block.isMultiblock()){ if(block.isMultiblock()){
@@ -292,7 +285,7 @@ public class Tile implements PosTrait, TargetTrait{
* Returns the list of all tiles linked to this multiblock if it were this block, or an empty array if it's not a multiblock. * Returns the list of all tiles linked to this multiblock if it were this block, or an empty array if it's not a multiblock.
* This array contains all linked tiles, including this tile itself. * This array contains all linked tiles, including this tile itself.
*/ */
public synchronized Array<Tile> getLinkedTilesAs(Block block, Array<Tile> tmpArray){ public Array<Tile> getLinkedTilesAs(Block block, Array<Tile> tmpArray){
tmpArray.clear(); tmpArray.clear();
if(block.isMultiblock()){ if(block.isMultiblock()){
int offsetx = -(block.size - 1) / 2; int offsetx = -(block.size - 1) / 2;
@@ -394,18 +387,14 @@ public class Tile implements PosTrait, TargetTrait{
} }
private void preChanged(){ private void preChanged(){
synchronized(tileSetLock){
block().removed(this); block().removed(this);
if(entity != null){ if(entity != null){
entity.removeFromProximity(); entity.removeFromProximity();
} }
team = 0; team = 0;
} }
}
private void changed(){ private void changed(){
synchronized(tileSetLock){
if(entity != null){ if(entity != null){
entity.remove(); entity.remove();
entity = null; entity = null;
@@ -438,7 +427,6 @@ public class Tile implements PosTrait, TargetTrait{
} }
updateOcclusion(); updateOcclusion();
}
world.notifyChanged(this); world.notifyChanged(this);
} }