IEvent uses volatile array instead of a list, fixes multithreading modification errors

This commit is contained in:
UnknownShadow200 2017-07-12 14:19:05 +10:00
parent 348b62f5f7
commit 0128e3c3b2
16 changed files with 85 additions and 74 deletions

View File

@ -17,10 +17,9 @@
*/
using System;
namespace MCGalaxy
{
public sealed partial class Block
{
namespace MCGalaxy {
public sealed partial class Block {
public static string Name(byte block) { return Props[block].Name; }
public static byte Byte(string type) {

View File

@ -15,12 +15,11 @@
or implied. See the Licenses for the specific language governing
permissions and limitations under the Licenses.
*/
using System;
using System.IO;
using System.Text;
using MCGalaxy.Blocks;
using MCGalaxy.Network;
using Newtonsoft.Json;
using System;
using System.IO;
using MCGalaxy.Blocks;
using MCGalaxy.Network;
using Newtonsoft.Json;
namespace MCGalaxy {
public sealed class BlockDefinition {

View File

@ -16,7 +16,6 @@
permissions and limitations under the Licenses.
*/
using System;
using System.Collections.Generic;
using System.IO;
namespace MCGalaxy.Blocks {

View File

@ -17,7 +17,6 @@
*/
using System;
using System.Text;
using MCGalaxy.Blocks;
namespace MCGalaxy.Blocks {

View File

@ -275,7 +275,7 @@ namespace MCGalaxy.Commands.Fun {
public override void Help(Player p) {
Player.Message(p, "%T/cd joins/leave %H- joins/leaves the game");
Player.Message(p, "%T/cd join/leave %H- joins/leaves the game");
Player.Message(p, "%T/cd players %H- lists players currently playing");
Player.Message(p, "%T/cd rules %H- view the rules of countdown");
if (CheckExtraPerm(p, 1)) {
@ -289,7 +289,7 @@ namespace MCGalaxy.Commands.Fun {
Player.Message(p, "%H speed can be: slow, normal, fast, extreme or ultimate");
Player.Message(p, "%H mode can be: normal or freeze");
Player.Message(p, "%T/cd end %H- force ends current round of countdown");
Player.Message(p, "%T/cd reset %H- resets the map. Note %T/cd start %Hauto does this.");
Player.Message(p, "%T/cd reset %H- resets the map. %T/cd start %Halso resets map.");
}
}
}

View File

@ -25,7 +25,7 @@ namespace MCGalaxy.Events.EconomyEvents {
public sealed class OnMoneyChangedEvent : IEvent<OnMoneyChanged> {
public static void Call(Player p) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p));
CallCommon(pl => pl(p));
}
}
@ -34,7 +34,7 @@ namespace MCGalaxy.Events.EconomyEvents {
public sealed class OnEcoTransactionEvent : IEvent<OnEcoTransaction> {
public static void Call(EcoTransaction transaction) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(transaction));
CallCommon(pl => pl(transaction));
}
}
}

View File

@ -25,7 +25,7 @@ namespace MCGalaxy.Events.GroupEvents {
public sealed class OnGroupLoadedEvent : IEvent<GroupLoaded> {
public static void Call(Group g) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(g));
CallCommon(pl => pl(g));
}
}
@ -34,7 +34,7 @@ namespace MCGalaxy.Events.GroupEvents {
public sealed class OnGroupLoadEvent : IEvent<GroupLoad> {
public static void Call() {
if (handlers.Count == 0) return;
CallImpl(pl => pl());
CallCommon(pl => pl());
}
}
@ -43,7 +43,7 @@ namespace MCGalaxy.Events.GroupEvents {
public sealed class OnGroupSaveEvent : IEvent<GroupSave> {
public static void Call() {
if (handlers.Count == 0) return;
CallImpl(pl => pl());
CallCommon(pl => pl());
}
}
@ -52,7 +52,7 @@ namespace MCGalaxy.Events.GroupEvents {
public sealed class OnChangingGroupEvent : IEvent<OnChangingGroup> {
public static void Call(string player, Group curRank, Group newRank) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(player, curRank, newRank));
CallCommon(pl => pl(player, curRank, newRank));
}
}
}

View File

@ -14,7 +14,7 @@
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the Licenses for the specific language governing
permissions and limitations under the Licenses.
*/
*/
using System;
using System.Collections.Generic;
@ -23,7 +23,7 @@ namespace MCGalaxy.Events {
/// <remarks> *** You MUST use a DIFFERENT delegate type for each subclass *** <br/><br/>
/// This is because the static event lists are unique to each new generic type instantiation, not each new subclass. </remarks>
public class IEvent<IMethod> {
protected internal static List<IEvent<IMethod>> handlers = new List<IEvent<IMethod>>();
protected internal static VolatileArray<IEvent<IMethod>> handlers = new VolatileArray<IEvent<IMethod>>();
protected IMethod method;
protected Priority priority;
@ -36,21 +36,23 @@ namespace MCGalaxy.Events {
IEvent<IMethod> handler = new IEvent<IMethod>();
handler.method = method; handler.priority = priority;
handlers.Add(handler);
SortHandlers();
AddHandler(handler);
}
/// <summary> Unregisters the given handler from this event. </summary>
public static void Unregister(IMethod method) {
if (Find(method) == null)
IEvent<IMethod> handler = Find(method);
if (handler == null)
throw new ArgumentException("Method was not registered as a handler!");
handlers.Remove(Find(method));
handlers.Remove(handler);
}
public static IEvent<IMethod> Find(IMethod method) {
Delegate methodDel = (Delegate)((object)method);
foreach (var p in handlers) {
IEvent<IMethod>[] items = handlers.Items;
foreach (var p in items) {
Delegate pMethodDel = (Delegate)((object)p.method);
if (pMethodDel == methodDel) return p;
}
@ -58,27 +60,40 @@ namespace MCGalaxy.Events {
}
protected static void SortHandlers() {
handlers.Sort((a, b) => b.priority.CompareTo(a.priority));
static void AddHandler(IEvent<IMethod> handler) {
// We want both the add and sorting is in one step
lock (handlers.locker) {
IEvent<IMethod>[] old = handlers.Items;
IEvent<IMethod>[] items = new IEvent<IMethod>[old.Length + 1];
for (int i = 0; i < old.Length; i++) {
items[i] = old[i];
}
items[old.Length] = handler;
Array.Sort(items, (a, b) => b.priority.CompareTo(a.priority));
handlers.Items = items;
}
}
protected static void CallImpl(Action<IMethod> action) {
try {
foreach (var pl in handlers) {
try {
action(pl.method);
} catch (Exception ex) {
Logger.Log(LogType.Warning, "Plugin {0} errored when calling {1} event",
GetFullMethodName(pl.method), typeof(IMethod).Name);
Logger.LogError(ex);
}
protected static void CallCommon(Action<IMethod> action) {
IEvent<IMethod>[] items = handlers.Items;
for (int i = 0; i < items.Length; i++) {
IEvent<IMethod> handler = items[i];
try {
action(handler.method);
} catch (Exception ex) {
LogHandlerException(ex, handler);
}
} catch (Exception ex) {
Logger.Log(LogType.Warning, "Error when calling {0} event", typeof(IMethod).Name);
Logger.LogError(ex);
}
}
protected static void LogHandlerException(Exception ex, IEvent<IMethod> handler) {
Logger.Log(LogType.Warning, "Plugin {0} errored when calling {1} event",
GetFullMethodName(handler.method), typeof(IMethod).Name);
Logger.LogError(ex);
}
static string GetFullMethodName(object method) {
Delegate del = (Delegate)((object)method);
return del.Method.ReflectedType.FullName + "." + del.Method.Name;

View File

@ -24,7 +24,7 @@ namespace MCGalaxy.Events.LevelEvents {
public sealed class OnLevelLoadedEvent : IEvent<OnLevelLoaded> {
public static void Call(Level lvl) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(lvl));
CallCommon(pl => pl(lvl));
}
}
@ -32,7 +32,7 @@ namespace MCGalaxy.Events.LevelEvents {
public sealed class OnLevelLoadEvent : IEvent<OnLevelLoad> {
public static void Call(string name) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(name));
CallCommon(pl => pl(name));
}
}
@ -40,7 +40,7 @@ namespace MCGalaxy.Events.LevelEvents {
public sealed class OnLevelSaveEvent : IEvent<OnLevelSave> {
public static void Call(Level lvl) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(lvl));
CallCommon(pl => pl(lvl));
}
}
@ -48,7 +48,7 @@ namespace MCGalaxy.Events.LevelEvents {
public sealed class OnLevelUnloadEvent : IEvent<OnLevelUnload> {
public static void Call(Level lvl) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(lvl));
CallCommon(pl => pl(lvl));
}
}
@ -56,7 +56,7 @@ namespace MCGalaxy.Events.LevelEvents {
public sealed class OnPhysicsStateChangedEvent : IEvent<OnPhysicsStateChanged> {
public static void Call(Level lvl, PhysicsState state) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(lvl, state));
CallCommon(pl => pl(lvl, state));
}
}
@ -64,7 +64,7 @@ namespace MCGalaxy.Events.LevelEvents {
public sealed class OnPhysicsUpdateEvent : IEvent<OnPhysicsUpdate> {
public static void Call(ushort x, ushort y, ushort z, PhysicsArgs extraInfo, Level l) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(x, y, z, extraInfo, l));
CallCommon(pl => pl(x, y, z, extraInfo, l));
}
}
}

View File

@ -133,7 +133,7 @@ namespace MCGalaxy.Events {
public sealed class OnModActionEvent : IEvent<OnModAction> {
public static void Call(ModAction e) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(e));
CallCommon(pl => pl(e));
}
}
}

View File

@ -27,7 +27,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnPlayerChatEvent : IEvent<OnPlayerChat> {
public static void Call(Player p, string message) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, message));
CallCommon(pl => pl(p, message));
}
}
@ -36,7 +36,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnPlayerMoveEvent : IEvent<OnPlayerMove> {
public static void Call(Player p, Position next, byte yaw, byte pitch) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, next, yaw, pitch));
CallCommon(pl => pl(p, next, yaw, pitch));
}
}
@ -45,7 +45,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnSQLSaveEvent : IEvent<OnSQLSave> {
public static void Call(Player p, string ysqlcommand) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, ysqlcommand));
CallCommon(pl => pl(p, ysqlcommand));
}
}
@ -55,7 +55,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnPlayerCommandEvent : IEvent<OnPlayerCommand> {
public static void Call(Player p, string cmd, string args) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, cmd, args));
CallCommon(pl => pl(p, cmd, args));
}
}
@ -64,7 +64,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnPlayerConnectEvent: IEvent<OnPlayerConnect> {
public static void Call(Player p) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p));
CallCommon(pl => pl(p));
}
}
@ -73,7 +73,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnPlayerConnectingEvent: IEvent<OnPlayerConnecting> {
public static void Call(Player p, string mppass) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, mppass));
CallCommon(pl => pl(p, mppass));
}
}
@ -82,7 +82,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnPlayerDeathEvent : IEvent<OnPlayerDeath> {
public static void Call(Player p, ExtBlock block) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, block));
CallCommon(pl => pl(p, block));
}
}
@ -91,7 +91,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnPlayerDisconnectEvent : IEvent<OnPlayerDisconnect> {
public static void Call(Player p, string reason) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, reason));
CallCommon(pl => pl(p, reason));
}
}
@ -100,7 +100,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnBlockChangeEvent : IEvent<OnBlockChange> {
public static void Call(Player p, ushort x, ushort y, ushort z, ExtBlock block) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, x, y, z, block));
CallCommon(pl => pl(p, x, y, z, block));
}
}
@ -113,7 +113,7 @@ namespace MCGalaxy.Events.PlayerEvents {
ushort yaw, ushort pitch, byte entity,
ushort x, ushort y, ushort z, TargetBlockFace face) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, button, action, yaw,
CallCommon(pl => pl(p, button, action, yaw,
pitch, entity, x, y, z, face));
}
}
@ -123,7 +123,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnMessageRecievedEvent : IEvent<OnMessageReceived> {
public static void Call(Player p, string message) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, message));
CallCommon(pl => pl(p, message));
}
}
@ -132,7 +132,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnJoinedLevelEvent : IEvent<OnJoinedLevel> {
public static void Call(Player p, Level prevLevl, Level level) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, prevLevl, level));
CallCommon(pl => pl(p, prevLevl, level));
}
}
@ -143,7 +143,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public static void Call(Player p, PlayerAction action,
string message = null, bool stealth = false) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, action, message, stealth));
CallCommon(pl => pl(p, action, message, stealth));
}
}
@ -152,7 +152,7 @@ namespace MCGalaxy.Events.PlayerEvents {
public sealed class OnSendingMotdEvent : IEvent<OnSendingMotd> {
public static void Call(Player p, byte[] packet) {
if (handlers.Count == 0) return;
CallImpl(pl => pl(p, packet));
CallCommon(pl => pl(p, packet));
}
}
}

View File

@ -35,7 +35,7 @@ namespace MCGalaxy.Games {
public int Points;
/// <summary> Players on this team. </summary>
public VolatileArray<Player> Members = new VolatileArray<Player>(false);
public VolatileArray<Player> Members = new VolatileArray<Player>();
/// <summary> Position in the world the flag is located at. </summary>

View File

@ -24,10 +24,10 @@ namespace MCGalaxy.Games {
public sealed class CountdownGame : IGame {
/// <summary> All players who are playing this countdown game. </summary>
public VolatileArray<Player> Players = new VolatileArray<Player>(false);
public VolatileArray<Player> Players = new VolatileArray<Player>();
/// <summary> Players who are still alive in the current round. </summary>
public VolatileArray<Player> Remaining = new VolatileArray<Player>(false);
public VolatileArray<Player> Remaining = new VolatileArray<Player>();
/// <summary> Map countdown is running on. </summary>
public Level Map;

View File

@ -70,10 +70,10 @@ namespace MCGalaxy.Games {
public Level CurLevel = null;
/// <summary> List of alive/human players. </summary>
public VolatileArray<Player> Alive = new VolatileArray<Player>(false);
public VolatileArray<Player> Alive = new VolatileArray<Player>();
/// <summary> List of dead/infected players. </summary>
public VolatileArray<Player> Infected = new VolatileArray<Player>(false);
public VolatileArray<Player> Infected = new VolatileArray<Player>();
public List<string> RecentMaps = new List<string>();
@ -92,10 +92,10 @@ namespace MCGalaxy.Games {
int infectCombo = 0;
/// <summary> List of players who have a bounty on them. </summary>
public VolatileArray<BountyData> Bounties = new VolatileArray<BountyData>(false);
public VolatileArray<BountyData> Bounties = new VolatileArray<BountyData>();
/// <summary> List of players who are in the lottery. </summary>
public VolatileArray<string> Lottery = new VolatileArray<string>(false);
public VolatileArray<string> Lottery = new VolatileArray<string>();
}
public static class ZSConfig {

View File

@ -140,7 +140,7 @@ namespace MCGalaxy {
public bool staticCommands = false;
public DateTime ZoneSpam;
public VolatileArray<SchedulerTask> CriticalTasks = new VolatileArray<SchedulerTask>(false);
public VolatileArray<SchedulerTask> CriticalTasks = new VolatileArray<SchedulerTask>();
public bool aiming;
public bool isFlying = false;
@ -187,7 +187,7 @@ namespace MCGalaxy {
public bool spawned = false;
//Undo
internal VolatileArray<UndoDrawOpEntry> DrawOps = new VolatileArray<UndoDrawOpEntry>(false);
internal VolatileArray<UndoDrawOpEntry> DrawOps = new VolatileArray<UndoDrawOpEntry>();
internal readonly object pendingDrawOpsLock = new object();
internal List<PendingDrawOp> PendingDrawOps = new List<PendingDrawOp>();

View File

@ -36,7 +36,7 @@ namespace MCGalaxy {
readonly bool useList;
public VolatileArray(bool useList) {
public VolatileArray(bool useList = false) {
this.useList = useList;
if (useList) list = new List<T>();
}