-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
351 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package timedCraft; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import net.minecraft.entity.player.EntityPlayer; | ||
import net.minecraft.entity.player.EntityPlayerMP; | ||
|
||
public class PlayerTimeManager { | ||
private static final String dataAllocatedLabel = "TimedCraft_DataAllocated"; | ||
private static final String remainingTimeLabel = "TimedCraft_RemainingTime"; | ||
private static final String lastLogoutTimeLabel = "TimedCraft_LastLogoutTime"; | ||
private static final String lastTickTimeLabel = "TimedCraft_LastTickTime"; | ||
|
||
private static final int playerNotificationInterval = 300000; | ||
private static final int playerTimeNearlyUpNotificationTime = 60000; | ||
|
||
private static PlayerTimeManager instance = null; | ||
|
||
// A redundant data store that we read from whenever the player's stored | ||
// entity data is missing, such as on respawn after death. | ||
private Map<String, Long> cachedPlayerLastTickTime = new HashMap<String, Long>(); | ||
private Map<String, Integer> cachedPlayerTimeRemaining = new HashMap<String, Integer>(); | ||
|
||
public static PlayerTimeManager GetInstance() { | ||
if (instance == null) { | ||
instance = new PlayerTimeManager(); | ||
} | ||
|
||
return instance; | ||
} | ||
|
||
public void OnPlayerConnect(EntityPlayer player) { | ||
if (TimedConfiguration.Instance.PlayerHasTimeLimit(player)) { | ||
this.EnsurePlayerDataAllocated(player); | ||
|
||
// Credit the player for time accumulated since last login | ||
this.AddTimeAccumulatedSinceLastLogout(player); | ||
|
||
// Message the player with their time remaining | ||
this.SendPlayerTimeNotification(player); | ||
} | ||
} | ||
|
||
public void OnPlayerDisconnect(EntityPlayer player) { | ||
if (TimedConfiguration.Instance.PlayerHasTimeLimit(player)) { | ||
this.EnsurePlayerDataAllocated(player); | ||
|
||
// Record the player's logout time | ||
this.RecordLastLogout(player); | ||
|
||
// Flush the last tick time | ||
player.getEntityData().setLong(lastTickTimeLabel, 0); | ||
} | ||
} | ||
|
||
public void OnPlayerTick(EntityPlayer player) { | ||
// If we have a time limit, enforce it | ||
if (TimedConfiguration.Instance.PlayerHasTimeLimit(player)) { | ||
this.EnsurePlayerDataAllocated(player); | ||
|
||
// Measure the time between ticks and update the stored last tick value | ||
long lastTickTime = player.getEntityData().getLong(lastTickTimeLabel); | ||
long currentTickTime = System.currentTimeMillis(); | ||
|
||
// Only tick the player if we have a last tick | ||
if (lastTickTime > 0) { | ||
int millisecondsSinceLastTick = (int) (currentTickTime - player.getEntityData().getLong(lastTickTimeLabel)); | ||
|
||
int previousTimeRemaining = this.GetPlayerRemainingTime(player); | ||
|
||
this.AddTime(player, (int)(millisecondsSinceLastTick * this.GetPlayerTimeRegenerationRate(player))); | ||
this.SubtractTime(player, millisecondsSinceLastTick); | ||
|
||
int currentTimeRemaining = this.GetPlayerRemainingTime(player); | ||
|
||
if (currentTimeRemaining <= 0) { | ||
this.KickPlayer(player); | ||
} else if (currentTimeRemaining % playerNotificationInterval > previousTimeRemaining % playerNotificationInterval) { | ||
this.SendPlayerTimeNotification(player); | ||
} else if (currentTimeRemaining < playerTimeNearlyUpNotificationTime && previousTimeRemaining > playerTimeNearlyUpNotificationTime) { | ||
this.SendPlayerTimeNotification(player); | ||
} | ||
} | ||
|
||
player.getEntityData().setLong(lastTickTimeLabel, currentTickTime); | ||
this.cachedPlayerLastTickTime.put(player.username, currentTickTime); | ||
} | ||
} | ||
|
||
// If the player's data is not yet allocated (which occurs either when a new player | ||
// joins or the player dies), assign it to either a cached value if we have one or | ||
// the defaults. Defaults are: | ||
// - remaining time is the player's maximum daily time | ||
// - last tick time is null | ||
// - last logout is always null (this is zero while the player is logged in, so it doesn't matter if he dies) | ||
private void EnsurePlayerDataAllocated(EntityPlayer player) { | ||
boolean dataAllocated = player.getEntityData().getBoolean(dataAllocatedLabel); | ||
if (!dataAllocated) { | ||
TimedCraft.Logger.info(String.format("Allocating data for player %s.", player.username)); | ||
|
||
player.getEntityData().setBoolean(dataAllocatedLabel, true); | ||
|
||
if (this.cachedPlayerTimeRemaining.containsKey(player.username)) { | ||
player.getEntityData().setInteger(remainingTimeLabel, this.cachedPlayerTimeRemaining.get(player.username)); | ||
} else { | ||
player.getEntityData().setInteger(remainingTimeLabel, TimedConfiguration.Instance.GetPlayerDailyTimeAllotment(player)); | ||
} | ||
|
||
if (this.cachedPlayerLastTickTime.containsKey(player.username)) { | ||
player.getEntityData().setLong(lastTickTimeLabel, this.cachedPlayerLastTickTime.get(player.username)); | ||
} else { | ||
player.getEntityData().setLong(lastTickTimeLabel, 0); | ||
} | ||
|
||
player.getEntityData().setLong(lastLogoutTimeLabel, 0); | ||
} | ||
} | ||
|
||
// | ||
private void AddTimeAccumulatedSinceLastLogout(EntityPlayer player) { | ||
long lastLogout = player.getEntityData().getLong(lastLogoutTimeLabel); | ||
if (lastLogout > 0) | ||
{ | ||
int millisecondsSinceLogout = (int)(System.currentTimeMillis() - lastLogout); | ||
int millisecondsAccumulated = (int)(millisecondsSinceLogout * this.GetPlayerTimeRegenerationRate(player)); | ||
|
||
this.AddTime(player, millisecondsAccumulated); | ||
|
||
// Clear the last logout | ||
player.getEntityData().setLong(lastLogoutTimeLabel, 0); | ||
} | ||
} | ||
|
||
private void RecordLastLogout(EntityPlayer player) { | ||
player.getEntityData().setLong(lastLogoutTimeLabel, System.currentTimeMillis()); | ||
} | ||
|
||
// Adds the given amount of time to the player, up to the per-day cap. | ||
private void AddTime(EntityPlayer player, int milliseconds) { | ||
int currentTimeRemaining = this.GetPlayerRemainingTime(player); | ||
currentTimeRemaining = Math.min(currentTimeRemaining + milliseconds, TimedConfiguration.Instance.GetPlayerDailyTimeAllotment(player)); | ||
this.SetPlayerRemainingTime(player, currentTimeRemaining); | ||
} | ||
|
||
// Subtracts the given amount of time from the player, down to zero. | ||
private void SubtractTime(EntityPlayer player, int milliseconds) { | ||
int currentTimeRemaining = this.GetPlayerRemainingTime(player); | ||
currentTimeRemaining = Math.max(currentTimeRemaining - milliseconds, 0); | ||
this.SetPlayerRemainingTime(player, currentTimeRemaining); | ||
} | ||
|
||
// Gets the rate at which a player regains play time, compared to the normal passage of time. | ||
private double GetPlayerTimeRegenerationRate(EntityPlayer player) { | ||
return TimedConfiguration.Instance.GetPlayerDailyTimeAllotment(player) / 86400000.0; | ||
} | ||
|
||
// Gets the time, in milliseconds, that the player has left to be logged in. | ||
private int GetPlayerRemainingTime(EntityPlayer player) { | ||
return player.getEntityData().getInteger(remainingTimeLabel); | ||
} | ||
|
||
// Sets the player's remaining logged-in time, in milliseconds. | ||
private void SetPlayerRemainingTime(EntityPlayer player, int milliseconds) { | ||
player.getEntityData().setInteger(remainingTimeLabel, milliseconds); | ||
this.cachedPlayerTimeRemaining.put(player.username, milliseconds); | ||
} | ||
|
||
private void SendPlayerTimeNotification(EntityPlayer player) { | ||
int remainingTime = (int) this.GetPlayerRemainingTime(player); | ||
player.addChatMessage(String.format("You have %d:%02d minutes of gameplay time remaining.", remainingTime / 60000, (remainingTime / 1000) % 60 )); | ||
} | ||
|
||
private void KickPlayer(EntityPlayer player) { | ||
((EntityPlayerMP)player).playerNetServerHandler.kickPlayerFromServer("Your time is up!"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package timedCraft; | ||
|
||
import java.util.HashMap; | ||
import java.util.Iterator; | ||
import java.util.Map; | ||
|
||
import net.minecraft.entity.player.EntityPlayer; | ||
import net.minecraftforge.common.ConfigCategory; | ||
import net.minecraftforge.common.Configuration; | ||
|
||
public class TimedConfiguration { | ||
public static TimedConfiguration Instance = null; | ||
|
||
public static void Load(Configuration config) { | ||
try { | ||
config.load(); | ||
|
||
ConfigCategory category = config.getCategory(Configuration.CATEGORY_GENERAL); | ||
Instance = new TimedConfiguration(category); | ||
|
||
TimedCraft.Logger.info("Successfully loaded configuration."); | ||
} catch (Exception e) { | ||
// We didn't successfully read the configuration file | ||
TimedCraft.Logger.info("Failed to load configuration, defaulting."); | ||
Instance = new TimedConfiguration(); | ||
} finally { | ||
config.save(); | ||
} | ||
} | ||
|
||
private Map<String, Integer> timeLimits; | ||
|
||
private TimedConfiguration() | ||
{ | ||
timeLimits = new HashMap<String, Integer>(); | ||
} | ||
|
||
private TimedConfiguration(ConfigCategory category) | ||
{ | ||
timeLimits = new HashMap<String, Integer>(); | ||
|
||
Iterator<String> keyIterator = category.keySet().iterator(); | ||
while (keyIterator.hasNext()) { | ||
String currentKey = keyIterator.next(); | ||
timeLimits.put(currentKey, category.get(currentKey).getInt()); | ||
} | ||
} | ||
|
||
// Returns true if the player has a time limit configured, false otherwise. | ||
public boolean PlayerHasTimeLimit(EntityPlayer player) { | ||
return this.timeLimits.containsKey(player.username); | ||
} | ||
|
||
// Gets the time, in milliseconds, allowed to the player per day. Returns -1 if the player | ||
// is not configured to have a time limit. | ||
public int GetPlayerDailyTimeAllotment(EntityPlayer player) { | ||
if (this.timeLimits.containsKey(player.username)) { | ||
return this.timeLimits.get(player.username); | ||
} else { | ||
return -1; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Configuration file | ||
|
||
#################### | ||
# general | ||
#################### | ||
|
||
general { | ||
I:blastron=5400000 | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package timedCraft; | ||
|
||
import java.util.logging.Logger; | ||
|
||
import net.minecraftforge.common.Configuration; | ||
import cpw.mods.fml.common.FMLLog; | ||
import cpw.mods.fml.common.Mod; | ||
import cpw.mods.fml.common.Mod.EventHandler; | ||
import cpw.mods.fml.common.Mod.Instance; | ||
import cpw.mods.fml.common.event.FMLInitializationEvent; | ||
import cpw.mods.fml.common.event.FMLPreInitializationEvent; | ||
import cpw.mods.fml.common.network.NetworkMod; | ||
import cpw.mods.fml.common.registry.GameRegistry; | ||
import cpw.mods.fml.common.registry.TickRegistry; | ||
import cpw.mods.fml.relauncher.Side; | ||
|
||
@Mod(modid = TimedCraft.modid, name="TimedCraft", version="0.1") | ||
@NetworkMod(clientSideRequired = false, serverSideRequired = false) | ||
public class TimedCraft { | ||
public static final String modid = "TimedCraft"; | ||
|
||
@Instance(modid) | ||
public static TimedCraft Instance; | ||
|
||
public static Logger Logger; | ||
|
||
@EventHandler | ||
public void preInit(FMLPreInitializationEvent event) { | ||
Logger = Logger.getLogger(modid); | ||
Logger.setParent(FMLLog.getLogger()); | ||
|
||
Configuration config = new Configuration(event.getSuggestedConfigurationFile()); | ||
TimedConfiguration.Load(config); | ||
} | ||
|
||
@EventHandler | ||
public void load(FMLInitializationEvent event) { | ||
GameRegistry.registerPlayerTracker(new TimedPlayerTracker()); | ||
TickRegistry.registerTickHandler(new TimedTickHandler(), Side.SERVER); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package timedCraft; | ||
|
||
import net.minecraft.entity.player.EntityPlayer; | ||
import cpw.mods.fml.common.IPlayerTracker; | ||
|
||
public class TimedPlayerTracker implements IPlayerTracker { | ||
|
||
@Override | ||
public void onPlayerLogin(EntityPlayer player) { | ||
PlayerTimeManager.GetInstance().OnPlayerConnect(player); | ||
} | ||
|
||
@Override | ||
public void onPlayerLogout(EntityPlayer player) { | ||
PlayerTimeManager.GetInstance().OnPlayerDisconnect(player); | ||
} | ||
|
||
@Override | ||
public void onPlayerChangedDimension(EntityPlayer player) { | ||
// Do nothing. | ||
} | ||
|
||
@Override | ||
public void onPlayerRespawn(EntityPlayer player) { | ||
// Do nothing. | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package timedCraft; | ||
|
||
import java.util.EnumSet; | ||
|
||
import net.minecraft.entity.player.EntityPlayer; | ||
import cpw.mods.fml.common.ITickHandler; | ||
import cpw.mods.fml.common.TickType; | ||
|
||
public class TimedTickHandler implements ITickHandler { | ||
|
||
@Override | ||
public void tickStart(EnumSet<TickType> type, Object... tickData) { | ||
// Do nothing | ||
} | ||
|
||
@Override | ||
public void tickEnd(EnumSet<TickType> type, Object... tickData) { | ||
EntityPlayer tickedPlayer = (EntityPlayer) tickData[0]; | ||
PlayerTimeManager.GetInstance().OnPlayerTick(tickedPlayer); | ||
} | ||
|
||
@Override | ||
public EnumSet<TickType> ticks() { | ||
return EnumSet.of(TickType.PLAYER); | ||
} | ||
|
||
@Override | ||
public String getLabel() { | ||
return "TimedCraft TimedTickHandler"; | ||
} | ||
|
||
} |