//stores replay, springbattle, processes rating and xp, returns whether battle ended normally
        public static bool SubmitSpringBattleResult(SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server, Action <BattleDebriefing> consumer)
        {
            var ret = new BattleDebriefing();

            try
            {
                bool isValidGame = true;
                if (!result.GameEndedOk)
                {
                    ret.Message = "Game didn't end properly";
                    isValidGame = false;
                }
                if (result.IsCheating)
                {
                    ret.Message = "Cheats were enabled during this game";
                    isValidGame = false;
                }

                var db   = new ZkDataContext();
                var text = new StringBuilder();

                var sb = SaveSpringBattle(result, db);

                if (isValidGame)
                {
                    StoreAwards(result.OutputExtras, sb, db);
                }
                StoreLogs(result.OutputExtras, sb, db);

                if (result.LobbyStartContext.Mode == AutohostMode.Planetwars)
                {
                    ProcessPlanetWars(result, server, sb, db, text);
                }


                Dictionary <int, int> orgLevels = sb.SpringBattlePlayers.Select(x => x.Account).ToDictionary(x => x.AccountID, x => x.Level);

                //fill in applicable ratings
                bool noElo = !isValidGame || result.LobbyStartContext.ModOptions.Any(x => x.Key.ToLower() == "noelo" && x.Value != "0" && x.Value != "false");
                if (!noElo)
                {
                    RatingSystems.FillApplicableRatings(sb, result);
                }

                if (isValidGame)
                {
                    ProcessXP(result, server, db, sb);
                }


                ret.Url            = string.Format("{1}/Battles/Detail/{0}", sb.SpringBattleID, GlobalConst.BaseSiteUrl);
                ret.ServerBattleID = sb.SpringBattleID;

                server.GhostSay(
                    new Say()
                {
                    Text =
                        string.Format("BATTLE DETAILS AND REPLAY ----> {0} <-----", ret.Url),
                    IsEmote = true,
                    Place   = SayPlace.Battle,
                    User    = GlobalConst.NightwatchName
                },
                    result.LobbyStartContext.BattleID);


                foreach (var p in sb.SpringBattlePlayers.Where(x => !x.IsSpectator))
                {
                    ret.DebriefingUsers[p.Account.Name] = new BattleDebriefing.DebriefingUser()
                    {
                        AccountID       = p.AccountID,
                        LoseTime        = p.LoseTime ?? -1,
                        AllyNumber      = p.AllyNumber,
                        IsInVictoryTeam = p.IsInVictoryTeam,
                        EloChange       = 0,
                        IsRankdown      = false,
                        IsRankup        = false,
                        NewElo          = -1,
                        NextRankElo     = -1,
                        PrevRankElo     = -1,
                        NewRank         = p.Account.Rank,
                        XpChange        = p.XpChange ?? 0,
                        NewXp           = p.Account.Xp,
                        NextLevelXp     = Account.GetXpForLevel(p.Account.Level + 1),
                        PrevLevelXp     = Account.GetXpForLevel(p.Account.Level),
                        IsLevelUp       = orgLevels[p.AccountID] < p.Account.Level,
                        Awards          = sb.AccountBattleAwards.Where(x => x.AccountID == p.AccountID).Select(x => new BattleDebriefing.DebriefingAward()
                        {
                            Value       = x.Value,
                            Key         = x.AwardKey,
                            Description = x.AwardDescription
                        }).ToList()
                    };
                }

                //send to rating

                if (!noElo)
                {
                    RatingSystems.ProcessResult(sb, result, new PendingDebriefing()
                    {
                        debriefingConsumer = consumer,
                        partialDebriefing  = ret,
                        battle             = sb,
                    });
                }

                Trace.TraceInformation("Battle ended: Server exited for B" + sb.SpringBattleID);
                db.SaveChanges();

                return(true);
            }
            catch (Exception ex)
            {
                var data = JsonConvert.SerializeObject(result);
                Trace.TraceError($"{ex}\nData:\n{data}");
                ret.Message = "Error processing game result: " + ex.Message;
                return(false);
            }
        }