Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c69aae4

Browse files
committedMar 18, 2017
Finish and improve fluid implementation
1 parent ed4eabd commit c69aae4

9 files changed

+272
-57
lines changed
 

‎src/main/java/nova/core/component/fluid/Fluid.java

+23-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
package nova.core.component.fluid;
2222

2323
import nova.core.block.BlockFactory;
24+
import nova.core.component.exception.ComponentException;
25+
import nova.core.component.misc.FactoryProvider;
2426
import nova.core.retention.Data;
2527
import nova.core.retention.Storable;
2628
import nova.core.retention.Store;
@@ -29,19 +31,20 @@
2931

3032
import java.util.Optional;
3133

34+
// TODO: Should this extend ComponentProvider?
3235
public class Fluid implements Identifiable, Storable, Cloneable {
3336
/**
3437
* 1000 liters = 1 cubic meter
3538
*/
36-
public static final int bucketVolume = 1000;
39+
public static final int BUCKET_VOLUME = 1000;
3740
/**
3841
* Fluid amount is measured in liters.
3942
*/
4043
@Store(key = "amount")
4144
private int amount = 1;
4245

4346
//TODO: Public instance variable is not good practice
44-
public FluidFactory factory;
47+
private FluidFactory factory = null;
4548

4649
/**
4750
* @return Amount of fluid
@@ -104,7 +107,7 @@ public Fluid withAmount(int amount) {
104107
* @return The block. There may be no block associated with this fluid.
105108
*/
106109
public Optional<BlockFactory> getBlockFactory() {
107-
return Optional.empty();
110+
return Game.blocks().get(getID());
108111
}
109112

110113
@Override
@@ -125,9 +128,25 @@ public boolean sameType(Fluid stack) {
125128
return stack.getID().equals(getID());
126129
}
127130

131+
void initFactory(FluidFactory factory) {
132+
if (factory == null) {
133+
this.factory = factory;
134+
} else {
135+
throw new ComponentException("Attempt to add two components of the type %s to " + this, FactoryProvider.class);
136+
}
137+
}
138+
139+
public final FluidFactory getFactory() {
140+
if (factory != null) {
141+
return factory;
142+
} else {
143+
throw new ComponentException("Attempt to get component that does not exist: %s", FactoryProvider.class);
144+
}
145+
}
146+
128147
@Override
129148
public final String getID() {
130-
return factory.getID();
149+
return getFactory().getID();
131150
}
132151

133152
@Override

‎src/main/java/nova/core/component/fluid/FluidFactory.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public FluidFactory(String id, Supplier<Fluid> constructor) {
5454
@Override
5555
public Fluid build() {
5656
Fluid build = super.build();
57-
build.factory = this;
57+
build.initFactory(this);
5858
return build;
5959
}
6060

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (c) 2017 NOVA, All rights reserved.
3+
* This library is free software, licensed under GNU Lesser General Public License version 3
4+
*
5+
* This file is part of NOVA.
6+
*
7+
* NOVA is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* NOVA is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with NOVA. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package nova.core.component.fluid;
22+
23+
import java.util.function.Predicate;
24+
25+
/**
26+
* A filter that only accepts a specific sub-type of {@link Fluid}. For use with tanks.
27+
*
28+
* @author ExE Boss
29+
*/
30+
@FunctionalInterface
31+
public interface FluidFilter extends Predicate<Fluid> {
32+
33+
/**
34+
* Returns an {@link FluidFilter} that accepts an {@link Fluid} of the same
35+
* type as the provided.
36+
*
37+
* @param item
38+
* @return ItemFilter
39+
*/
40+
static FluidFilter of(Fluid fluid) {
41+
return fluid::sameType;
42+
}
43+
44+
/**
45+
* Returns an {@link FluidFilter} that accepts an {@link Fluid} of the same
46+
* type as provided.
47+
*
48+
* @param id
49+
* @return ItemFilter
50+
*/
51+
static FluidFilter of(String id) {
52+
return (other) -> id.equals(other.getID());
53+
}
54+
55+
/**
56+
* Accepts any {@link Fluid} that has a &gt;= amount than provided.
57+
*
58+
* @param amount
59+
* @return
60+
*/
61+
static FluidFilter of(int amount) {
62+
return (other) -> other.amount() >= amount;
63+
}
64+
}

‎src/main/java/nova/core/component/fluid/FluidHandler.java

+119-6
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,142 @@
1616
*
1717
* You should have received a copy of the GNU General Public License
1818
* along with NOVA. If not, see <http://www.gnu.org/licenses/>.
19-
*/package nova.core.component.fluid;
19+
*/
20+
21+
package nova.core.component.fluid;
2022

2123
import nova.core.component.Component;
22-
import nova.core.util.Direction;
24+
import nova.core.component.SidedComponent;
2325

2426
import java.util.Arrays;
2527
import java.util.HashSet;
28+
import java.util.Optional;
2629
import java.util.Set;
27-
import java.util.function.Function;
30+
import java.util.function.Predicate;
2831

2932
/**
33+
* A sided component that provides a fluid container.
3034
* @author Calclavia
3135
*/
32-
public class FluidHandler extends Component {
36+
@SidedComponent
37+
public class FluidHandler extends Component implements FluidConsumer, FluidProvider {
3338

34-
public Set<Tank> tanks = new HashSet<>();
35-
public Function<Direction, Set<Tank>> sidedTanks = direction -> tanks;
39+
protected Set<Tank> tanks = new HashSet<>();
40+
protected boolean resizable;
41+
42+
public static FluidHandler singleTank() {
43+
return new FluidHandler(false, new TankSimple(Fluid.BUCKET_VOLUME));
44+
}
45+
46+
public static FluidHandler singleTank(int capacity) {
47+
return new FluidHandler(false, new TankSimple(capacity));
48+
}
49+
50+
public static FluidHandler singleTank(Predicate<Fluid> fluidFilter) {
51+
return new FluidHandler(false, new TankSimple(Fluid.BUCKET_VOLUME).setFluidFilter(fluidFilter));
52+
}
53+
54+
public static FluidHandler singleTank(int capacity, Predicate<Fluid> fluidFilter) {
55+
return new FluidHandler(false, new TankSimple(capacity).setFluidFilter(fluidFilter));
56+
}
3657

3758
public FluidHandler() {
59+
this.resizable = true;
3860
}
3961

4062
public FluidHandler(Tank... tanks) {
63+
this(true, tanks);
64+
}
65+
66+
public FluidHandler(boolean resizable, Tank... tanks) {
4167
this.tanks.addAll(Arrays.asList(tanks));
68+
this.resizable = resizable;
69+
if (resizable) {
70+
this.tanks.removeIf(Tank::isEmpty);
71+
}
72+
}
73+
74+
@Override
75+
public int addFluid(Fluid fluid, boolean simulate) {
76+
if (fluid.amount() == 0)
77+
return 0;
78+
79+
Fluid f = fluid.clone();
80+
int added = 0;
81+
82+
for (Tank tank : tanks) {
83+
int a = tank.addFluid(f, simulate);
84+
added += a;
85+
f.remove(a);
86+
if (f.amount() == 0)
87+
break;
88+
}
89+
90+
if (f.amount() > 0 && resizable) {
91+
Tank t = new TankSimple();
92+
added += t.addFluid(f);
93+
tanks.add(t);
94+
}
95+
96+
return added;
97+
}
98+
99+
@Override
100+
public Optional<Fluid> removeFluid(Fluid fluid, boolean simulate) {
101+
if (fluid.amount() == 0)
102+
return Optional.empty();
103+
104+
Fluid f = fluid.withAmount(0);
105+
Fluid r = fluid.clone();
106+
107+
for (Tank tank : tanks) {
108+
if (!tank.hasFluidType(fluid))
109+
continue;
110+
111+
int removed = tank.removeFluid(r).get().amount();
112+
r.remove(removed);
113+
f.add(removed);
114+
115+
if (r.amount() == 0)
116+
break;
117+
}
118+
119+
if (resizable) {
120+
this.tanks.removeIf(Tank::isEmpty);
121+
}
122+
123+
return Optional.of(f).filter(fl -> fl.amount() > 0);
42124
}
43125

126+
@Override
127+
public Optional<Fluid> removeFluid(int amount, boolean simulate) {
128+
Optional<Fluid> fluid = tanks.stream()
129+
.filter(Tank::hasFluid)
130+
.findFirst()
131+
.flatMap(Tank::getFluid)
132+
.map(f -> f.withAmount(0));
133+
134+
if (amount == 0 || !fluid.isPresent())
135+
return fluid;
136+
137+
Fluid f = fluid.get();
138+
139+
for (Tank tank : tanks) {
140+
if (!tank.hasFluidType(f))
141+
continue;
142+
143+
int removed = tank.removeFluid(amount).get().amount();
144+
amount -= removed;
145+
f.add(removed);
146+
147+
if (amount == 0)
148+
break;
149+
}
150+
151+
if (resizable) {
152+
this.tanks.removeIf(Tank::isEmpty);
153+
}
154+
155+
return Optional.of(f).filter(fl -> fl.amount() > 0);
156+
}
44157
}

‎src/main/java/nova/core/component/fluid/FluidManager.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import nova.core.util.registry.FactoryManager;
2424
import nova.core.util.registry.Registry;
25+
import nova.internal.core.Game;
2526

2627
import java.util.Optional;
2728
import java.util.function.Function;
@@ -61,6 +62,12 @@ public FluidFactory register(FluidFactory factory) {
6162

6263
@Override
6364
public void init() {
64-
//TODO: Implement
65+
Game.events().publish(new Init(this));
66+
}
67+
68+
public class Init extends ManagerEvent<FluidManager> {
69+
public Init(FluidManager manager) {
70+
super(manager);
71+
}
6572
}
6673
}

‎src/main/java/nova/core/component/fluid/FluidProvider.java

+17
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,23 @@
2727
* @see FluidConsumer
2828
*/
2929
public interface FluidProvider {
30+
/**
31+
* Attempt to extract fluid from this FluidProvider
32+
* @param fluid The fluid type and max amount to extract
33+
* @param simulate Whether to simulate the extraction
34+
* @return Extracted {@link Fluid}
35+
*/
36+
Optional<Fluid> removeFluid(Fluid fluid, boolean simulate);
37+
38+
/**
39+
* Attempt to extract fluid from this FluidProvider
40+
* @param amount Amount of fluid to extract
41+
* @return Extracted {@link Fluid}
42+
*/
43+
default Optional<Fluid> removeFluid(Fluid fluid) {
44+
return removeFluid(fluid, false);
45+
}
46+
3047
/**
3148
* Attempt to extract fluid from this FluidProvider
3249
* @param amount Amount of fluid to extract

‎src/main/java/nova/core/component/fluid/SidedTankProvider.java

-34
This file was deleted.

‎src/main/java/nova/core/component/fluid/Tank.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,14 @@ default int getFluidAmount() {
4444
}
4545

4646
/**
47-
* @return Whether this container is storing a fluid
47+
* @return Whether this container is empty
48+
*/
49+
default boolean isEmpty() {
50+
return !getFluid().isPresent();
51+
}
52+
53+
/**
54+
* @return Whether this container is storing a fluid (is not empty)
4855
*/
4956
default boolean hasFluid() {
5057
return getFluid().isPresent();

‎src/main/java/nova/core/component/fluid/TankSimple.java

+32-10
Original file line numberDiff line numberDiff line change
@@ -24,34 +24,46 @@
2424
import nova.core.network.Syncable;
2525
import nova.core.retention.Data;
2626
import nova.core.retention.Storable;
27+
import nova.core.util.math.MathUtil;
2728

2829
import java.util.Optional;
30+
import java.util.OptionalInt;
31+
import java.util.function.Predicate;
2932

3033
/**
3134
* This class provides basic implementation of {@link Tank}
3235
*/
3336
public class TankSimple implements Tank, Storable, Syncable {
3437

3538
private Optional<Fluid> containedFluid = Optional.empty();
36-
private int capacity;
39+
private OptionalInt capacity;
40+
private Predicate<Fluid> fluidFilter = f -> true;
3741

3842
public TankSimple() {
39-
this(Fluid.bucketVolume);
43+
this.capacity = OptionalInt.empty();
4044
}
4145

4246
public TankSimple(int maxCapacity) {
43-
this.capacity = maxCapacity;
47+
this.capacity = OptionalInt.of(maxCapacity);
48+
}
49+
50+
public TankSimple removeCapacity() {
51+
this.capacity = OptionalInt.empty();
52+
return this;
4453
}
4554

4655
public TankSimple setCapacity(int capacity) {
47-
this.capacity = capacity;
56+
this.capacity = OptionalInt.of(capacity);
4857
setFluid(containedFluid);
4958
return this;
5059
}
5160

5261
@Override
5362
public int addFluid(Fluid fluid, boolean simulate) {
54-
int capacity = this.capacity - containedFluid.orElse(fluid.withAmount(0)).amount();
63+
if (fluid.amount() == 0 || !fluidFilter.test(fluid))
64+
return 0;
65+
66+
int capacity = this.capacity.orElse(Integer.MAX_VALUE) - containedFluid.orElseGet(() -> fluid.withAmount(0)).amount();
5567
int toPut = Math.min(fluid.amount(), capacity);
5668

5769
if (containedFluid.isPresent()) {
@@ -72,6 +84,14 @@ public int addFluid(Fluid fluid, boolean simulate) {
7284
}
7385
}
7486

87+
@Override
88+
public Optional<Fluid> removeFluid(Fluid fluid, boolean simulate) {
89+
if (!containedFluid.filter(fluid::sameType).isPresent())
90+
return Optional.empty();
91+
92+
return removeFluid(fluid.amount(), simulate);
93+
}
94+
7595
@Override
7696
public Optional<Fluid> removeFluid(int amount, boolean simulate) {
7797
if (!containedFluid.isPresent()) {
@@ -98,19 +118,21 @@ public Optional<Fluid> removeFluid(int amount, boolean simulate) {
98118

99119
@Override
100120
public int getFluidCapacity() {
101-
return capacity;
121+
return capacity.orElse(Integer.MAX_VALUE);
102122
}
103123

104124
@Override
105125
public Optional<Fluid> getFluid() {
106126
return containedFluid;
107127
}
108128

129+
public TankSimple setFluidFilter(Predicate<Fluid> fluidFilter) {
130+
this.fluidFilter = fluidFilter;
131+
return this;
132+
}
133+
109134
public TankSimple setFluid(Optional<Fluid> fluid) {
110-
this.containedFluid = fluid;
111-
if (containedFluid.isPresent()) {
112-
containedFluid.get().setAmount(Math.max(Math.min(containedFluid.get().amount(), capacity), 0));
113-
}
135+
containedFluid = fluid.filter(fluidFilter).map(f -> f.withAmount(MathUtil.clamp(f.amount(), 0, capacity.orElse(Integer.MAX_VALUE)))).filter(f -> f.amount() > 0);
114136
return this;
115137
}
116138

0 commit comments

Comments
 (0)
Please sign in to comment.