mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-09-22 13:01:12 +03:00
Merge remote-tracking branch 'origin/master' into v9
# Conflicts: # core/src/mindustry/mod/DataImagePacker.java
This commit is contained in:
@@ -20,6 +20,7 @@ import javax.lang.model.element.*;
|
||||
import javax.lang.model.type.*;
|
||||
import java.lang.annotation.*;
|
||||
import java.util.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
@SupportedAnnotationTypes({
|
||||
"mindustry.annotations.Annotations.EntityDef",
|
||||
@@ -29,6 +30,11 @@ import java.util.*;
|
||||
"mindustry.annotations.Annotations.TypeIOHandler"
|
||||
})
|
||||
public class EntityProcess extends BaseProcessor{
|
||||
static final Pattern selfParamPattern = Pattern.compile("this\\.<(.*)>self\\(\\)");
|
||||
static final Pattern selfPattern = Pattern.compile("self\\(\\)");
|
||||
static final Pattern yieldPattern = Pattern.compile(" yield ");
|
||||
static final Pattern missingPattern = Pattern.compile("\\/\\*missing\\*\\/");
|
||||
|
||||
Seq<EntityDefinition> definitions = new Seq<>();
|
||||
Seq<GroupDefinition> groupDefs = new Seq<>();
|
||||
Seq<Stype> baseComponents;
|
||||
@@ -77,12 +83,12 @@ public class EntityProcess extends BaseProcessor{
|
||||
for(Smethod elem : component.methods()){
|
||||
if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE)) continue;
|
||||
//get all statements in the method, store them
|
||||
methodBlocks.put(elem.descString(), elem.tree().getBody().toString()
|
||||
.replaceAll("this\\.<(.*)>self\\(\\)", "this") //fix parameterized self() calls
|
||||
.replaceAll("self\\(\\)", "this") //fix self() calls
|
||||
.replaceAll(" yield ", "") //fix enchanced switch
|
||||
.replaceAll("\\/\\*missing\\*\\/", "var") //fix vars
|
||||
);
|
||||
String body = elem.tree().getBody().toString();
|
||||
body = selfParamPattern.matcher(body).replaceAll("this"); //fix parameterized self() calls
|
||||
body = selfPattern.matcher(body).replaceAll("this"); //fix self() calls
|
||||
body = yieldPattern.matcher(body).replaceAll(""); //fix enhanced switch
|
||||
body = missingPattern.matcher(body).replaceAll("var"); //fix vars
|
||||
methodBlocks.put(elem.descString(), body);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,7 +572,15 @@ public class EntityProcess extends BaseProcessor{
|
||||
String blockName = elem.up().getSimpleName().toString().toLowerCase().replace("comp", "");
|
||||
|
||||
//skip empty blocks
|
||||
if(str.replace("{", "").replace("\n", "").replace("}", "").replace("\t", "").replace(" ", "").isEmpty()){
|
||||
boolean empty = true;
|
||||
for(int i = 0; i < str.length(); i++){
|
||||
char c = str.charAt(i);
|
||||
if(c != '{' && c != '}' && c != '\n' && c != '\t' && c != ' '){
|
||||
empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(empty){
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -3016,7 +3016,7 @@ lenum.payenter = 进入/降落到单位下方的荷载方块中
|
||||
lenum.flag = 赋予单位数字形式的标记
|
||||
lenum.mine = 从某个位置采集矿物
|
||||
lenum.build = 建造建筑
|
||||
lenum.getblock = 根据坐标获取建筑物、环境块和环境墙体类型。\n单位必须在位置范围内,否则返回空值。
|
||||
lenum.getblock = 根据坐标获取建筑物、环境块和环境墙体类型。\n坐标必须在单位的雷达范围内,否则返回空值。
|
||||
lenum.within = 检查单位是否接近了某个位置
|
||||
lenum.boost = 开始/停止助推
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.defense.*;
|
||||
import mindustry.world.blocks.units.UnitAssembler.*;
|
||||
|
||||
import static arc.graphics.g2d.Draw.rect;
|
||||
@@ -2807,7 +2808,10 @@ public class Fx{
|
||||
shieldBreak = new Effect(40, e -> {
|
||||
color(e.color);
|
||||
stroke(3f * e.fout());
|
||||
Lines.poly(e.x, e.y, e.data instanceof Integer i ? i : 6, e.rotation + e.fin());
|
||||
int sides = e.data instanceof ForceProjector f ? f.sides : e.data instanceof ForceFieldAbility a ? a.sides : 6;
|
||||
float rotation = e.data instanceof ForceProjector f ? f.shieldRotation : e.data instanceof ForceFieldAbility a ? a.rotation : 6;
|
||||
|
||||
Lines.poly(e.x, e.y, sides, e.rotation + e.fin(), rotation);
|
||||
}).followParent(true),
|
||||
|
||||
arcShieldBreak = new Effect(40, e -> {
|
||||
|
||||
@@ -476,7 +476,7 @@ public class Logic implements ApplicationListener{
|
||||
|
||||
PerfCounter.unitUpdate.begin();
|
||||
if(editor){
|
||||
Groups.unit.update(Unitc::isPlayer);
|
||||
Groups.unit.update(u -> u.isPlayer() || u.spawnedByCore);
|
||||
}else{
|
||||
Groups.unit.update();
|
||||
}
|
||||
|
||||
@@ -168,7 +168,11 @@ public class NetClient implements ApplicationListener{
|
||||
try(DataInputStream in = new DataInputStream(data.stream)){
|
||||
String name = in.readUTF();
|
||||
byte[] pngData = in.readAllBytes();
|
||||
if(!headless) state.data.addTexture(name, pngData);
|
||||
if(!headless){
|
||||
//empty image data means we're removing the texture instead. see NetServer.removeTexture()
|
||||
if(pngData.length == 0) state.data.removeTexture(name);
|
||||
else state.data.addTexture(name, pngData);
|
||||
}
|
||||
}catch(IOException e){
|
||||
Log.err("Failed to read server texture stream", e);
|
||||
}
|
||||
|
||||
@@ -554,34 +554,36 @@ public class NetServer implements ApplicationListener{
|
||||
|
||||
/**
|
||||
* Streams a texture to a single connected client. This may take some time if the image is large or if the connection is poor.
|
||||
* Make sure to call {@link mindustry.mod.DataManager#removeTexture} when the image is no longer needed to prevent resource leaks.
|
||||
* Use {@link PixmapIO#writePngBytes} to get Pixmap bytes. Respect {@link mindustry.mod.DataPatcher#maxImageSize}. */
|
||||
* Make sure to call {@link #removeTexture(NetConnection, String)} when the image is no longer needed to prevent resource leaks.
|
||||
* Use {@link PixmapIO#writePngBytes} to get Pixmap bytes. Respect {@link mindustry.mod.DataPatcher#maxImageSize}.
|
||||
* Should be called on main thread to ensure correct ordering of sends (multiple with same name), and removals (remove called right after adding). */
|
||||
public void sendTexture(NetConnection con, String name, byte[] pngData){
|
||||
mainExecutor.submit(() -> {
|
||||
var stream = packTexture(name, pngData);
|
||||
con.sendStreamAsync(new TextureStream(), stream);
|
||||
});
|
||||
}
|
||||
|
||||
/** Streams a texture to every connected client. See {@link #sendTexture(NetConnection, String, byte[])} for more info. */
|
||||
public void sendTexture(String name, byte[] pngData){
|
||||
mainExecutor.submit(() -> {
|
||||
var stream = packTexture(name, pngData);
|
||||
for(NetConnection con : net.getConnections()){
|
||||
con.sendStreamAsync(new TextureStream(), stream);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ByteArrayOutputStream packTexture(String name, byte[] pngData){
|
||||
var stream = new ByteArrayOutputStream();
|
||||
try(DataOutputStream out = new DataOutputStream(stream)){
|
||||
out.writeUTF(name);
|
||||
out.write(pngData);
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
NetworkIO.packTexture(stream, name, pngData);
|
||||
con.sendStreamAsync(new TextureStream(), stream);
|
||||
}
|
||||
|
||||
/** Streams a texture to every connected client.
|
||||
* See {@link #sendTexture(NetConnection, String, byte[])} for more info. */
|
||||
public void sendTexture(String name, byte[] pngData){
|
||||
var stream = new ByteArrayOutputStream();
|
||||
NetworkIO.packTexture(stream, name, pngData);
|
||||
for(NetConnection con : net.getConnections()){
|
||||
con.sendStreamAsync(new TextureStream(), stream);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
/** Removes a texture previously sent with {@link #sendTexture(NetConnection, String, byte[])} from a single client.
|
||||
* If called while the texture is in use, it will be replaced with a black rectangle. Do not do this.
|
||||
* Should be called on main thread for the same reasons as sendTexture. */
|
||||
public void removeTexture(NetConnection con, String name){
|
||||
sendTexture(con, name, Streams.emptyBytes);
|
||||
}
|
||||
|
||||
/** Removes a texture previously sent with {@link #sendTexture(String, byte[])} from every connected client.
|
||||
* See {@link #removeTexture(NetConnection, String)} for more info. */
|
||||
public void removeTexture(String name){
|
||||
sendTexture(name, Streams.emptyBytes);
|
||||
}
|
||||
|
||||
public void addPacketHandler(String type, Cons2<Player, String> handler){
|
||||
|
||||
@@ -171,7 +171,7 @@ public class MapContentView implements AssetView{
|
||||
}
|
||||
|
||||
if(list.getChildren().isEmpty()){
|
||||
list.add("@patch.none");
|
||||
list.add("@none.found");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public class ForceFieldAbility extends Ability{
|
||||
if(unit.shield <= 0f && !wasBroken){
|
||||
unit.shield -= cooldown * regen;
|
||||
|
||||
Fx.shieldBreak.at(unit.x, unit.y, radius, unit.type.shieldColor(unit), sides);
|
||||
Fx.shieldBreak.at(unit.x, unit.y, radius, unit.type.shieldColor(unit), this);
|
||||
breakSound.at(unit.x, unit.y);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,16 +34,22 @@ public class ShieldArcAbility extends Ability{
|
||||
//translate bullet back to where it was upon collision
|
||||
b.trns(-b.vel.x, -b.vel.y);
|
||||
|
||||
float penX = Math.abs(paramPos.x - b.x), penY = Math.abs(paramPos.y - b.y);
|
||||
|
||||
if(penX > penY){
|
||||
b.vel.x *= -1;
|
||||
b.vel.y *= paramField.reflectVel;
|
||||
}else{
|
||||
b.vel.y *= -1;
|
||||
b.vel.x *= paramField.reflectVel;
|
||||
float nx = b.x - paramPos.x, ny = b.y - paramPos.y;
|
||||
float nlen = Mathf.len(nx, ny);
|
||||
if(nlen > 0.0001f){
|
||||
nx /= nlen;
|
||||
ny /= nlen;
|
||||
}
|
||||
|
||||
float dot = b.vel.x * nx + b.vel.y * ny;
|
||||
float rx = b.vel.x - 2f * dot * nx;
|
||||
float ry = b.vel.y - 2f * dot * ny;
|
||||
float outDot = rx * nx + ry * ny;
|
||||
float normalX = outDot * nx, normalY = outDot * ny;
|
||||
float tangX = rx - normalX, tangY = ry - normalY;
|
||||
|
||||
b.vel.set(normalX + tangX * paramField.reflectVel, normalY + tangY * paramField.reflectVel);
|
||||
|
||||
b.owner = paramUnit;
|
||||
b.team = paramUnit.team;
|
||||
b.time = b.lifetime * paramField.reflectTime;
|
||||
|
||||
@@ -27,7 +27,9 @@ public class DataImagePacker{
|
||||
|
||||
private @Nullable Seq<AtlasRegion> addedRegions;
|
||||
/** Textures added at runtime via addTexture(), keyed by their unprefixed name. Tracked separately from patchAtlas so they can be added/removed individually. */
|
||||
private ObjectMap<String, Texture> serverImages = new ObjectMap<>();
|
||||
private final ObjectMap<String, Texture> serverImages = new ObjectMap<>();
|
||||
/** Single threaded executor so that concurrent calls always complete in FIFO order. */
|
||||
private final ExecutorService textureExecutor = Threads.executor("Server Texture Streamer", 1);
|
||||
|
||||
/** Packs a new set of images. If images are already packed, disposes of the old ones. */
|
||||
public void pack(Seq<ImageAsset> images){
|
||||
@@ -175,7 +177,7 @@ public class DataImagePacker{
|
||||
|
||||
/** Decodes PNG bytes and registers them into the atlas under "netRegionPrefix + name", replacing any existing image with the same name. Safe to call from any thread. */
|
||||
public void addTexture(String name, byte[] pngData){
|
||||
Vars.mainExecutor.submit(() -> {
|
||||
textureExecutor.execute(() -> {
|
||||
Pixmap pixmap;
|
||||
try{
|
||||
pixmap = new Pixmap(pngData);
|
||||
@@ -208,8 +210,8 @@ public class DataImagePacker{
|
||||
});
|
||||
}
|
||||
|
||||
/** Removes a texture previously added with {@link #addTexture} */
|
||||
public void removeTexture(String name){
|
||||
/** Removes a texture previously added with {@link #addTexture}. Must be called on the main thread. */
|
||||
private void removeTexture(String name){
|
||||
Texture texture = serverImages.remove(name);
|
||||
if(texture != null){
|
||||
Core.atlas.getRegionMap().remove(serverRegionPrefix + name);
|
||||
@@ -219,6 +221,12 @@ public class DataImagePacker{
|
||||
}
|
||||
|
||||
public void printStats(PixmapPacker packer){
|
||||
/** Queues a texture for removal. Will run after any pending {@link #addTexture} calls so that races do not occur. */
|
||||
public void removeTextureQueued(String name){
|
||||
textureExecutor.execute(() -> Core.app.post(() -> removeTexture(name)));
|
||||
}
|
||||
|
||||
public void printStats(PixmapPacker mainPacker, PixmapPacker envPacker){
|
||||
if(Log.level != LogLevel.debug) return;
|
||||
|
||||
int total = packer.getPages().sum(p -> p.rects.size);
|
||||
|
||||
@@ -7,10 +7,10 @@ import arc.graphics.g2d.TextureAtlas.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.mod.data.*;
|
||||
import mindustry.net.*;
|
||||
|
||||
public class DataManager{
|
||||
private DataPatcher patcher = new DataPatcher();
|
||||
@@ -29,7 +29,6 @@ public class DataManager{
|
||||
|
||||
public void reloadContent(boolean reloadArrays){
|
||||
|
||||
patcher.unapply(reloadArrays);
|
||||
patcher.apply(getPatches(), getContent(), reloadArrays);
|
||||
|
||||
rebuildOrderedAssets();
|
||||
@@ -186,15 +185,15 @@ public class DataManager{
|
||||
}
|
||||
|
||||
/** Adds/replaces a single image pushed by the server at runtime, independent of map/mod data patches.
|
||||
* Use {@link mindustry.core.NetServer#sendTexture} to send a texture to connected clients. */
|
||||
* Use {@link mindustry.core.NetServer#sendTexture(NetConnection, String, byte[])} to send a texture to connected clients. */
|
||||
public void addTexture(String name, byte[] pngData){
|
||||
if(!Vars.headless) packer.addTexture(name, pngData);
|
||||
}
|
||||
|
||||
/** Removes a texture previously added with {@link #addTexture}. */
|
||||
@Remote(variants = Variant.both)
|
||||
public static void removeTexture(String name){
|
||||
if(!Vars.headless) Vars.state.data.packer.removeTexture(name);
|
||||
/** Removes a texture previously added with {@link #addTexture}.
|
||||
* Use {@link mindustry.core.NetServer#removeTexture(NetConnection, String)} to remove a texture from connected clients. */
|
||||
public void removeTexture(String name){
|
||||
if(!Vars.headless) packer.removeTextureQueued(name);
|
||||
}
|
||||
|
||||
public void reloadAudio(){
|
||||
|
||||
@@ -8,6 +8,7 @@ import arc.util.serialization.Json.*;
|
||||
import arc.util.serialization.*;
|
||||
import arc.util.serialization.Jval.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.part.*;
|
||||
@@ -92,7 +93,7 @@ public class DataPatcher{
|
||||
public void apply(Seq<PatchAsset> patches, Seq<ContentAsset> content, boolean reloadContentWorld){
|
||||
//if you're un-applying data patches, and it throws an error, just crash. this is not recoverable.
|
||||
if(applied){
|
||||
unapply();
|
||||
unapply(reloadContentWorld);
|
||||
applied = false;
|
||||
}
|
||||
|
||||
@@ -130,7 +131,8 @@ public class DataPatcher{
|
||||
Fi file = new Fi(asset.path);
|
||||
|
||||
//this is very important for resizing various arrays used in the game
|
||||
if((asset.type == ContentType.item || asset.type == ContentType.liquid)){
|
||||
//checking for blocks is also important, as those can be added/removed, and corresponding blocks need to be updated
|
||||
if(asset.type == ContentType.item || asset.type == ContentType.liquid || asset.type == ContentType.block){
|
||||
needsArrayFix = true;
|
||||
}
|
||||
|
||||
@@ -333,9 +335,27 @@ public class DataPatcher{
|
||||
if(!Vars.headless && Vars.ui != null && Vars.ui.editor != null && Vars.ui.editor.isShown()){
|
||||
int wh = Vars.world.width() * Vars.world.height();
|
||||
for(int i = 0; i < wh; i++){
|
||||
var b = Vars.world.tiles.geti(i).build;
|
||||
if(b != null && b.items != null) b.items.checkArrayCapacity(items);
|
||||
if(b != null && b.liquids != null) b.liquids.checkArrayCapacity(items);
|
||||
Tile tile = Vars.world.tiles.geti(i);
|
||||
|
||||
//stale checks for floor/overlay
|
||||
if(tile.floor().removed) tile.setFloor(getReplacementBlock(tile.floor()).asFloor());
|
||||
if(tile.overlay().removed) tile.setOverlay(getReplacementBlock(tile.overlay()).asFloor());
|
||||
|
||||
if(tile.block().removed){
|
||||
Block mapped = getReplacementBlock(tile.block());
|
||||
//tile refers to stale content; get rid of it.
|
||||
if(mapped == Blocks.air){
|
||||
tile.remove();
|
||||
}else{
|
||||
//update internal reference of block to point to the new one with correct ID
|
||||
tile.updateBlockReference(mapped);
|
||||
}
|
||||
}
|
||||
|
||||
var b = tile.build;
|
||||
if(b == null || !tile.isCenter()) continue;
|
||||
if(b.items != null) b.items.checkArrayCapacity(items);
|
||||
if(b.liquids != null) b.liquids.checkArrayCapacity(items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,6 +364,17 @@ public class DataPatcher{
|
||||
needsArrayFix = false;
|
||||
}
|
||||
|
||||
private static Block getReplacementBlock(Block existing){
|
||||
Block other = Vars.content.block(existing.name);
|
||||
if(other == null) return Blocks.air;
|
||||
//make sure they are type compatible
|
||||
if(other.getClass() == existing.getClass()){
|
||||
return other;
|
||||
}
|
||||
//could not find an equivalent, clear it
|
||||
return Blocks.air;
|
||||
}
|
||||
|
||||
void visit(Object object){
|
||||
visitStack.add(object);
|
||||
if(object instanceof Content c && usedpatches.add(c)){
|
||||
|
||||
@@ -426,6 +426,7 @@ public class ArcNetProvider implements NetProvider{
|
||||
|
||||
@Override
|
||||
public void sendStream(Streamable stream){
|
||||
//listeners are processed in the order they're added and each reads into the buffer greedily before the next gets a turn, so concurrent streams are sent in FIFO order
|
||||
connection.addListener(new InputStreamSender(stream.stream, 1024){
|
||||
int id;
|
||||
|
||||
|
||||
@@ -163,6 +163,15 @@ public class NetworkIO{
|
||||
}
|
||||
}
|
||||
|
||||
public static void packTexture(OutputStream os, String name, byte[] pngData){
|
||||
try(DataOutputStream stream = new DataOutputStream(os)){
|
||||
stream.writeUTF(name);
|
||||
stream.write(pngData);
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ByteBuffer writeServerData(){
|
||||
String name = (headless ? Config.serverName.string() : player.name);
|
||||
String description = headless && !Config.desc.string().equals("off") ? Config.desc.string() : "";
|
||||
|
||||
@@ -105,6 +105,9 @@ public class UiTreeBuilder{
|
||||
element.name = id;
|
||||
ctx.idElements.put(id, element);
|
||||
}
|
||||
String colorStr = child.str(UiKey.color); // Apply color if provided
|
||||
if(colorStr != null) element.setColor(Strings.parseColor(colorStr, Color.white));
|
||||
|
||||
stack.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +196,11 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
return overlay;
|
||||
}
|
||||
|
||||
/** Internal method for data patches - do not use!! */
|
||||
public void updateBlockReference(Block block){
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Block> T cblock(){
|
||||
return (T)block;
|
||||
|
||||
@@ -242,7 +242,7 @@ public class ForceProjector extends Block{
|
||||
if(buildup >= shieldHealth + phaseShieldBoost * phaseHeat && !broken){
|
||||
broken = true;
|
||||
buildup = shieldHealth;
|
||||
shieldBreakEffect.at(x, y, realRadius(), team.color, sides);
|
||||
shieldBreakEffect.at(x, y, realRadius(), team.color, block);
|
||||
breakSound.at(x, y);
|
||||
if(team != state.rules.defaultTeam){
|
||||
Events.fire(Trigger.forceProjectorBreak);
|
||||
|
||||
@@ -73,7 +73,7 @@ public class ShockwaveTower extends Block{
|
||||
if(potentialEfficiency > 0 && (reloadCounter += edelta()) >= reload && timer(timerCheck, checkInterval)){
|
||||
targets.clear();
|
||||
Groups.bullet.intersect(x - range, y - range, range * 2, range * 2, b -> {
|
||||
if(b.team != team && b.type.hittable){
|
||||
if(b.team != team && b.type.hittable && b.within(x, y, range + 1f)){
|
||||
targets.add(b);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,7 +53,7 @@ public class LiquidModule extends BlockModule{
|
||||
flow = cacheFlow;
|
||||
}
|
||||
|
||||
boolean updateFlow = flowTimer.get(15);
|
||||
boolean updateFlow = flowTimer.get(flowVisualRefreshInterval);
|
||||
|
||||
for(int i = 0; i < liquids.length; i++){
|
||||
flow[i].add(cacheSums[i]);
|
||||
@@ -63,7 +63,7 @@ public class LiquidModule extends BlockModule{
|
||||
cacheSums[i] = 0;
|
||||
|
||||
if(updateFlow){
|
||||
displayFlow[i] = flow[i].hasEnoughData() ? flow[i].mean() / flowVisualRefreshInterval : -1;
|
||||
displayFlow[i] = flow[i].hasEnoughData() ? flow[i].mean() / flowPollInterval : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user