Counter refactoring
This commit is contained in:
parent
a0fd852537
commit
e8370579c0
@ -8,6 +8,7 @@ import mineplex.core.hologram.Hologram;
|
||||
import mineplex.core.hologram.HologramManager;
|
||||
import mineplex.core.stats.StatsManager;
|
||||
import mineplex.serverdata.redis.counter.Counter;
|
||||
import mineplex.serverdata.redis.counter.GoalCounter;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@ -25,7 +26,7 @@ public class Fountain
|
||||
private final Location _location;
|
||||
private Location _lavaLocation;
|
||||
private final Hologram _hologram;
|
||||
private final Counter _counter;
|
||||
private final GoalCounter _counter;
|
||||
|
||||
private final FountainShop _shop;
|
||||
|
||||
@ -41,7 +42,7 @@ public class Fountain
|
||||
_location = location;
|
||||
_lavaLocation = lavaLocation;
|
||||
_hologram = new Hologram(hologramManager, location.clone().add(0, 2, 0), name).start();
|
||||
_counter = new Counter(dataKey, goal);
|
||||
_counter = new GoalCounter(dataKey, 5000, goal);
|
||||
|
||||
_shop = new FountainShop(this, fountainManager, clientManager, donationManager);
|
||||
|
||||
@ -56,19 +57,13 @@ public class Fountain
|
||||
|
||||
protected void updateCount()
|
||||
{
|
||||
// Make sure we only update if it has been 5 seconds since the last update
|
||||
// Players incrementing the counter will automatically cause it to update
|
||||
if (System.currentTimeMillis() - _counter.getCacheLastUpdated() > 5000)
|
||||
{
|
||||
_counter.updateCount();
|
||||
updateHologram();
|
||||
}
|
||||
updateHologram();
|
||||
}
|
||||
|
||||
public void increment(Player player, long amount)
|
||||
{
|
||||
_statsManager.incrementStat(player, "Global.Fountain." + getDataKey(), amount);
|
||||
_counter.increment(amount);
|
||||
_counter.addAndGet(amount);
|
||||
updateHologram();
|
||||
}
|
||||
|
||||
|
@ -3,73 +3,100 @@ package mineplex.serverdata.redis.counter;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* A counter represents an atomic number that is trying to hit a goal. Once the counter
|
||||
* reaches it's goal you can either reset it or let it stay completed for an indefinite time.
|
||||
* A counter represents an incrementing atomic number that is stored and updated through redis. This allows for
|
||||
* multiple servers to share and update the same counter
|
||||
*
|
||||
* @author Shaun Bennett
|
||||
*/
|
||||
public class Counter
|
||||
{
|
||||
// Cached count for the fountain
|
||||
private AtomicLong _count = new AtomicLong(0);
|
||||
// Cached count of the counter
|
||||
private final AtomicLong _count = new AtomicLong(0);
|
||||
// The System.currentTimeMillis() when cached count was last updated
|
||||
private long _cacheLastUpdated;
|
||||
// The goal that this fountain is trying to reach
|
||||
private final long _goal;
|
||||
private volatile long _lastUpdated;
|
||||
// The maximum time to wait before syncing the count with repository
|
||||
private final long _syncTime;
|
||||
// The unique key to reference this counter
|
||||
private final String _dataKey;
|
||||
|
||||
// Redis repository to store the count
|
||||
private CounterRedisRepository _redisRepository;
|
||||
private final CounterRedisRepository _redisRepository;
|
||||
|
||||
public Counter(String dataKey, long goal)
|
||||
public Counter(String dataKey, long syncTime)
|
||||
{
|
||||
_goal = goal;
|
||||
_dataKey = dataKey;
|
||||
_syncTime = syncTime;
|
||||
_redisRepository = new CounterRedisRepository(dataKey);
|
||||
|
||||
updateCount();
|
||||
}
|
||||
|
||||
public void updateCount()
|
||||
{
|
||||
updateCount(_redisRepository.getCount());
|
||||
}
|
||||
|
||||
protected void updateCount(long newCount)
|
||||
public Counter(String dataKey)
|
||||
{
|
||||
_count.set(newCount);
|
||||
_cacheLastUpdated = System.currentTimeMillis();
|
||||
this(dataKey, 5000); // 5 seconds
|
||||
}
|
||||
|
||||
public void increment(long amount)
|
||||
/**
|
||||
* Add a value to the counter and return the new counter value. This method is thread-safe and interacts
|
||||
* directly with the atomic value stored in redis. The value returned from redis is then returned
|
||||
*
|
||||
* addAndGet will also update the cached counter value so we don't need to make extra trips to redis
|
||||
*
|
||||
* @param amount the amount to add to the counter
|
||||
* @return the updated value of the counter from redis repository
|
||||
*/
|
||||
public long addAndGet(long amount)
|
||||
{
|
||||
long newCount = _redisRepository.incrementCount(amount);
|
||||
updateCount(newCount);
|
||||
return newCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest count of the counter. This will use the last cached value for the counter, or if
|
||||
* the last cached count hasn't been updated in the {@link #_syncTime} period it will pull the latest
|
||||
* count from the redis repository and use that value.
|
||||
*
|
||||
* @return The counter count
|
||||
*/
|
||||
public long getCount()
|
||||
{
|
||||
if (System.currentTimeMillis() - _lastUpdated > _syncTime)
|
||||
{
|
||||
updateCount(_redisRepository.getCount());
|
||||
}
|
||||
|
||||
return _count.get();
|
||||
}
|
||||
|
||||
public long getGoal()
|
||||
/**
|
||||
* Reset the counter back to 0. This will take a maximum time of {@link #_syncTime} to propagate
|
||||
* across all instances of this counter. Immediately updates the redis repository.
|
||||
*
|
||||
* @return The value of the counter before it was reset
|
||||
*/
|
||||
public long reset()
|
||||
{
|
||||
return _goal;
|
||||
}
|
||||
|
||||
public double getFillPercent()
|
||||
{
|
||||
return Math.min(1, (((double) getCount()) / _goal));
|
||||
updateCount(0);
|
||||
return _redisRepository.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data key for this counter. The data key is used as the redis repository key
|
||||
* @return The data key for this counter
|
||||
*/
|
||||
public String getDataKey()
|
||||
{
|
||||
return _dataKey;
|
||||
}
|
||||
|
||||
public long getCacheLastUpdated()
|
||||
/**
|
||||
* Update the cached count with a new value
|
||||
* @param newCount updated count
|
||||
*/
|
||||
protected void updateCount(long newCount)
|
||||
{
|
||||
return _cacheLastUpdated;
|
||||
_count.set(newCount);
|
||||
_lastUpdated = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
@ -42,10 +42,10 @@ public class CounterRedisRepository extends RedisRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the current fountain count by {@code increment} and then return the latest
|
||||
* count of the fountain. This is handled in an atomic process through redis
|
||||
* @param increment Amount to increment the fountain by
|
||||
* @return The new count for the fountain
|
||||
* Increment the current count by {@code increment} and then return the latest
|
||||
* count from redis. This is handled in an atomic process
|
||||
* @param increment Amount to increment counter by
|
||||
* @return The updated count from redis
|
||||
*/
|
||||
public long incrementCount(long increment)
|
||||
{
|
||||
@ -59,6 +59,26 @@ public class CounterRedisRepository extends RedisRepository
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the counter back to 0
|
||||
* @return the value of the counter before it was reset
|
||||
*/
|
||||
public long reset()
|
||||
{
|
||||
long count = -1;
|
||||
|
||||
try (Jedis jedis = getResource(true))
|
||||
{
|
||||
count = Long.parseLong(jedis.getSet(getKey(), "0"));
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key for this counter
|
||||
* @return The key is used to store the value in redis
|
||||
*/
|
||||
private String getKey()
|
||||
{
|
||||
return getKey(_dataKey);
|
||||
|
@ -0,0 +1,101 @@
|
||||
package mineplex.serverdata.redis.counter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A redis counter that is aiming to reach a goal
|
||||
*
|
||||
* @author Shaun Bennett
|
||||
*/
|
||||
public class GoalCounter extends Counter
|
||||
{
|
||||
// Has the goal been completed?
|
||||
private boolean _completed;
|
||||
// The goal we are trying to reach
|
||||
private long _goal;
|
||||
|
||||
private List<GoalCounterListener> _listeners;
|
||||
|
||||
public GoalCounter(String dataKey, long syncTime, long goal)
|
||||
{
|
||||
super(dataKey, syncTime);
|
||||
|
||||
_completed = false;
|
||||
_goal = goal;
|
||||
|
||||
_listeners = new ArrayList<>();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the progress towards the goal as a percent ranging from 0 to 1.
|
||||
* @return the percent progress towards goal
|
||||
*/
|
||||
public double getFillPercent()
|
||||
{
|
||||
return Math.min(1, (((double) getCount()) / _goal));
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the goal been completed?
|
||||
* @return
|
||||
*/
|
||||
public boolean isCompleted()
|
||||
{
|
||||
return _completed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the goal for this GoalCounter
|
||||
* @return the goal of this counter
|
||||
*/
|
||||
public long getGoal()
|
||||
{
|
||||
return _goal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener to this GoalCounter
|
||||
* @param listener the listener to be added
|
||||
*/
|
||||
public void addListener(GoalCounterListener listener)
|
||||
{
|
||||
_listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all the listeners
|
||||
*/
|
||||
public void clearListeners()
|
||||
{
|
||||
_listeners.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update {@link #_completed} and notify listeners if it has completed
|
||||
*/
|
||||
private void update()
|
||||
{
|
||||
boolean updatedValue = getCount() > _goal;
|
||||
|
||||
if (updatedValue != _completed)
|
||||
{
|
||||
_completed = updatedValue;
|
||||
_listeners.forEach(listener -> {
|
||||
if (_completed) listener.onGoalComplete(this);
|
||||
else listener.onGoalReset(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCount(long newCount)
|
||||
{
|
||||
super.updateCount(newCount);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package mineplex.serverdata.redis.counter;
|
||||
|
||||
/**
|
||||
* @author Shaun Bennett
|
||||
*/
|
||||
public interface GoalCounterListener
|
||||
{
|
||||
public void onGoalComplete(GoalCounter counter);
|
||||
|
||||
public void onGoalReset(GoalCounter counter);
|
||||
}
|
Loading…
Reference in New Issue
Block a user