aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.World/DurangoStats.cpp
diff options
context:
space:
mode:
authordaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
committerdaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
commitb691c43c44ff180d10e7d4a9afc83b98551ff586 (patch)
tree3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.World/DurangoStats.cpp
parentdef8cb415354ac390b7e89052a50605285f1aca9 (diff)
Initial commit
Diffstat (limited to 'Minecraft.World/DurangoStats.cpp')
-rw-r--r--Minecraft.World/DurangoStats.cpp1221
1 files changed, 1221 insertions, 0 deletions
diff --git a/Minecraft.World/DurangoStats.cpp b/Minecraft.World/DurangoStats.cpp
new file mode 100644
index 00000000..c9b51c84
--- /dev/null
+++ b/Minecraft.World/DurangoStats.cpp
@@ -0,0 +1,1221 @@
+#include "stdafx.h"
+
+#include "ItemStat.h"
+
+#include "Achievement.h"
+#include "Achievements.h"
+
+#include "DamageSource.h"
+#include "Player.h"
+#include "ItemInstance.h"
+#include "Tile.h"
+#include "Item.h"
+#include "Level.h"
+
+#include "..\Minecraft.Client\Minecraft.h"
+#include "LevelData.h"
+#include "LevelSettings.h"
+
+#include "..\Minecraft.Client\LocalPlayer.h"
+#include "..\Minecraft.Client\MultiPlayerLocalPlayer.h"
+
+#include "EntityIO.h"
+
+#include "..\Minecraft.Client\Durango\ServiceConfig\Events-XBLA.8-149E11AEEvents.h"
+
+#include "DurangoStats.h"
+
+ ///////////////////
+ // Ds Item Event //
+ ///////////////////
+
+string DsItemEvent::nameMethods[] = {
+ "NONE", "itemPickedUp", "itemCrafted",
+ "itemTakenFromChest", "itemTakenFromEnderchest",
+ "itemBought", "itemSmithed", "blockMined", "blockPlaced", "MAX"
+};
+
+DsItemEvent::DsItemEvent(int id, const wstring &name) : Stat(id,name) {}
+
+bool DsItemEvent::onLeaderboard(ELeaderboardId leaderboard, eAcquisitionMethod methodId, Param *param)
+{
+ switch (methodId)
+ {
+ case eAcquisitionMethod_Pickedup:
+ switch (param->itemId)
+ {
+ case Item::egg_Id:
+ case Tile::mushroom1_Id:
+ case Tile::mushroom2_Id:
+ return leaderboard == eLeaderboardId_FARMING;
+ }
+ break;
+
+ case eAcquisitionMethod_Mined:
+ switch (param->itemId)
+ {
+ case Tile::dirt_Id:
+ case Tile::stoneBrick_Id:
+ case Tile::sand_Id:
+ case Tile::rock_Id:
+ case Tile::gravel_Id:
+ case Tile::clay_Id:
+ case Tile::obsidian_Id:
+ return leaderboard == eLeaderboardId_MINING;
+
+ case Tile::crops_Id:
+ case Tile::pumpkin_Id:
+ case Tile::reeds_Id:
+ return leaderboard == eLeaderboardId_FARMING;
+ }
+ break;
+ }
+
+ return false;
+}
+
+
+// 4J-JEV, for tiles/items we want to record stats together.
+int DsItemEvent::mergeIds(int itemId)
+{
+ switch (itemId)
+ {
+ default:
+ return itemId;
+
+ case Tile::mushroom1_Id:
+ case Tile::mushroom2_Id:
+ return Tile::mushroom1_Id;
+
+ case Tile::dirt_Id:
+ case Tile::grass_Id:
+ case Tile::farmland_Id:
+ return Tile::dirt_Id;
+
+ case Tile::redstoneLight_Id:
+ case Tile::redstoneLight_lit_Id:
+ return Tile::redstoneLight_Id;
+
+ case Tile::redStoneOre_Id:
+ case Tile::redStoneOre_lit_Id:
+ return Tile::redStoneOre_Id;
+ }
+}
+
+void DsItemEvent::handleParamBlob(shared_ptr<LocalPlayer> player, byteArray paramBlob)
+{
+ if (paramBlob.length == sizeof(Param))
+ {
+ Param *param = (Param *) paramBlob.data;
+
+ // Combine block ids.
+ param->itemId = mergeIds( param->itemId );
+
+ // Send farming leaderboard updates.
+ if ( (param->methodId == eAcquisitionMethod_Pickedup && onLeaderboard(eLeaderboardId_FARMING, eAcquisitionMethod_Pickedup, param))
+ || (param->methodId == eAcquisitionMethod_Mined && onLeaderboard(eLeaderboardId_FARMING, eAcquisitionMethod_Mined, param)) )
+ {
+ EventWriteLeaderboardTotals(
+ DurangoStats::getUserId(player), //UserId
+ DurangoStats::getPlayerSession(), // PlayerSessionId
+ player->level->difficulty, // Difficulty,
+ eLeaderboardId_FARMING, // ScoreboardId
+ param->itemCount);
+
+ app.DebugPrintf("<%ls>\tscoreboardFarming(%i:%i:%i)\n", DurangoStats::getUserId(player),
+ player->level->difficulty, eLeaderboardId_FARMING, param->itemCount);
+ }
+
+ // Send mining leaderboard updates.
+ if ( param->methodId == eAcquisitionMethod_Mined && onLeaderboard(eLeaderboardId_MINING, eAcquisitionMethod_Mined, param) )
+ {
+ EventWriteLeaderboardTotals(
+ DurangoStats::getUserId(player), //UserId
+ DurangoStats::getPlayerSession(), // PlayerSessionId
+ player->level->difficulty, // Difficulty,
+ eLeaderboardId_MINING, // ScoreboardId
+ param->itemCount);
+ app.DebugPrintf("<%ls>\tscoreboardMining(%i:%i:%i)\n", DurangoStats::getUserId(player),
+ player->level->difficulty, eLeaderboardId_MINING, param->itemCount);
+ }
+
+ // Debug printout.
+ string method = nameMethods[(int)param->methodId];
+ app.DebugPrintf("<%ls>\t%s(%i:%i:%i)\n", DurangoStats::getUserId(player),
+ method.c_str(), param->itemId, param->itemAux, param->itemCount);
+
+ // Split on acquisition method, then send relevant events.
+ if (param->methodId == eAcquisitionMethod_Placed)
+ {
+ EventWriteBlockPlaced(
+ DurangoStats::getUserId(player),
+ DurangoStats::getPlayerSession(),
+ player->level->difficulty,
+ param->itemId,
+ param->itemAux,
+ param->itemCount
+ );
+ }
+ else if (param->methodId == eAcquisitionMethod_Mined)
+ {
+ EventWriteBlockBroken(
+ DurangoStats::getUserId(player),
+ DurangoStats::getPlayerSession(),
+ player->level->difficulty,
+ param->itemId,
+ param->itemAux,
+ param->itemCount
+ );
+ }
+ else
+ {
+ EventWriteMcItemAcquired(
+ DurangoStats::getUserId(player),
+ 0, // TODO
+ DurangoStats::getPlayerSession(),
+ 0,
+ 0,
+ player->level->difficulty,
+ param->itemId,
+ param->methodId,
+ 0, 0, 0, // (x,y,z)
+ param->itemAux,
+ param->itemCount
+ );
+ }
+ }
+}
+
+/* 4J-JEV: note that mined events will only fire with 'Instant_Mine' on if you are carrying an appropriate
+ * tool for the block that you are mining.
+ */
+byteArray DsItemEvent::createParamBlob(eAcquisitionMethod eMethod, int itemId, int itemAux, int itemCount)
+{
+ byteArray output;
+ Param param = { eMethod, itemId, itemAux, itemCount };
+ output.data = (byte *) new Param(param);
+ output.length = sizeof(Param);
+ return output;
+}
+
+
+ ///////////////////
+ // Ds Mob Killed //
+ ///////////////////
+
+DsMobKilled::DsMobKilled(int id, const wstring &name) : Stat(id,name) {}
+
+void DsMobKilled::handleParamBlob(shared_ptr<LocalPlayer> player, byteArray paramBlob)
+{
+ if (paramBlob.length == sizeof(Param))
+ {
+ Param *param = (Param *) paramBlob.data;
+
+ if (param->mobType < 0) return;
+
+ if (EventWriteMobKilled(DurangoStats::getUserId(player),
+ 0,
+ DurangoStats::getPlayerSession(),
+ DurangoStats::getMultiplayerCorrelationId(),
+ 0,
+ player->level->difficulty,
+ DurangoStats::getPlayerSession(), // ROUND ID
+ 0,
+ param->weaponId,
+ param->mobType,
+ param->isRanged?1:0,
+ 0, 0, 0, // (x,y,z),
+ 0,
+ param->distance,
+ param->mobType)
+ == 0)
+ {
+ app.DebugPrintf("<%ls>\t%s(%i:%i:%i:%i)\n", DurangoStats::getUserId(player),
+ (param->isRanged?"mobShotWithEntity":"mobKilledInMelee"),
+ param->mobType, param->weaponId, param->distance, param->damage);
+ }
+
+ switch(EntityIO::getClass(param->mobType))
+ {
+ case eTYPE_MONSTER:
+ if(param->mobType != SPIDER_JOCKEY_ID) break;
+ // Fallthrough is spider jockey
+ case eTYPE_ZOMBIE:
+ case eTYPE_SKELETON:
+ case eTYPE_CREEPER:
+ case eTYPE_SPIDER:
+ case eTYPE_PIGZOMBIE:
+ case eTYPE_SLIME:
+
+ if (EventWriteLeaderboardTotals(
+ DurangoStats::getUserId(player), //UserId
+ DurangoStats::getPlayerSession(), // PlayerSessionId
+ player->level->difficulty, // Difficulty,
+ eLeaderboardId_KILLING, // ScoreboardId
+ 1) // Count
+ == 0)
+ {
+ app.DebugPrintf("<%ls>\tscoreboardKills(%i:%i:1)\n", DurangoStats::getUserId(player),
+ player->level->difficulty, eLeaderboardId_KILLING);
+ }
+ }
+ }
+}
+
+byteArray DsMobKilled::createParamBlob(shared_ptr<Player> player, shared_ptr<Mob> mob, DamageSource *dmgSrc)
+{
+ // 4J-JEV: Get the id we use for Durango Server Stats.
+ int mob_networking_id;
+ eINSTANCEOF mobEType = mob->GetType();
+ if ( (mobEType == eTYPE_SPIDER) && (mob->rider.lock() != NULL) && (mob->rider.lock()->GetType() == eTYPE_SKELETON) )
+ {
+ mob_networking_id = SPIDER_JOCKEY_ID; // Spider jockey only a concept for leaderboards.
+ }
+ else if ( (mobEType == eTYPE_SKELETON) && (mob->riding != NULL) && (mob->riding->GetType() == eTYPE_SPIDER) )
+ {
+ mob_networking_id = SPIDER_JOCKEY_ID; // Spider jockey only a concept for leaderboards.
+ }
+ else
+ {
+ mob_networking_id = EntityIO::eTypeToIoid(mobEType);
+ }
+
+ // Kill made with projectile, arrow/ghast/fireball/snowball.
+ // NB: Snowball kills would make an awesome achievement. ("Not a snowball's chance...")
+ if ( dmgSrc->isProjectile() )
+ {
+ byteArray output;
+ Param param = {
+ DsMobKilled::RANGED,
+ mob_networking_id,
+ EntityIO::eTypeToIoid(dmgSrc->getDirectEntity()->GetType()),
+ mob->distanceTo(player->x, player->y, player->z),
+ 0/*not needed*/
+ };
+ output.data = (byte*) new Param(param);
+ output.length = sizeof(Param);
+ return output;
+ }
+
+ // Kill made in melee, use itemInHand as weapon.
+ shared_ptr<ItemInstance> item = player->getCarriedItem();
+ byteArray output;
+ Param param = {
+ DsMobKilled::MELEE,
+ mob_networking_id,
+ (item != NULL ? item->getItem()->id : 0),
+ mob->distanceTo(player->x, player->y, player->z),
+ 0/*not needed*/
+ };
+ output.data = (byte*) new Param(param);
+ output.length = sizeof(Param);
+ return output;
+}
+
+
+ /////////////////////
+ // Ds Mob Interact //
+ /////////////////////
+
+string DsMobInteract::nameInteract[] = {
+ "unknownMobInteraction", "mobBred", "mobTamed", "mobCured", "mobCrafted", "mobSheared"
+};
+
+DsMobInteract::DsMobInteract(int id, const wstring &name) : Stat(id,name) {}
+
+void DsMobInteract::handleParamBlob(shared_ptr<LocalPlayer> player, byteArray paramBlob)
+{
+ if (paramBlob.length == sizeof(Param))
+ {
+ Param *param = (Param *) paramBlob.data;
+
+ if (param->mobId < 0) return;
+
+ app.DebugPrintf("<%ls>\t%s(%i)\n", DurangoStats::getUserId(player),
+ nameInteract[param->interactionType].c_str(), param->mobId);
+
+ EventWriteMobInteract(
+ DurangoStats::getUserId(player),
+ DurangoStats::getPlayerSession(),
+ param->mobId,
+ param->interactionType );
+ }
+}
+
+byteArray DsMobInteract::createParamBlob(eInteract interactionId, int entityId)
+{
+ byteArray output;
+ Param param = { interactionId, EntityIO::eTypeToIoid((eINSTANCEOF)entityId) };
+ output.data = (byte*) new Param(param);
+ output.length = sizeof(Param);
+ return output;
+}
+
+
+ ///////////////
+ // Ds Travel //
+ ///////////////
+
+string DsTravel::nameMethods[eMethod_MAX] =
+ {
+ "Walk", "Swim", "Fall", "Climb", "Cart", "Boat", "Pig", "Time"
+ };
+
+unsigned int DsTravel::CACHE_SIZES[eMethod_MAX] =
+ {
+ 40, // WALK - Meters?
+ 20, // SWIM - Meters?
+ 0, // FALL - Meters? - Fall event naturally only sends on land, no caching necessary.
+ 10, // CLIMB - Meters?
+ 70, // CART - Meters?
+ 70, // BOAT - Meters?
+ 10, // PIG - Meters?
+ 20*60*5, // TIME - GameTicks (20*60*5 ~ 5 mins)
+ };
+
+DsTravel::DsTravel(int id, const wstring &name) : Stat(id,name)
+{
+ ZeroMemory(&param_cache, sizeof(unsigned int)*eMethod_MAX*MAX_LOCAL_PLAYERS);
+}
+
+void DsTravel::handleParamBlob(shared_ptr<LocalPlayer> player, byteArray paramBlob)
+{
+ if (paramBlob.length == sizeof(Param))
+ {
+ Param *param = (Param*) paramBlob.data;
+
+ int newDistance = cache(player->GetXboxPad(), *param);
+
+ if ( newDistance > 0 ) write(player, param->method, newDistance);
+ }
+}
+
+byteArray DsTravel::createParamBlob(eMethod method, int distance)
+{
+ byteArray output;
+ Param param = { method, distance };
+ output.data = (byte*) new Param(param);
+ output.length = sizeof(Param);
+ return output;
+}
+
+int DsTravel::cache(int iPad, Param &param)
+{
+ if ( (eMethod_walk <= param.method) && (param.method < eMethod_MAX) )
+ {
+ param_cache[iPad][param.method] += param.distance;
+
+ if (param_cache[iPad][param.method] > CACHE_SIZES[param.method])
+ {
+ int out = param_cache[iPad][param.method];
+ param_cache[iPad][param.method] = 0;
+ return out;
+ }
+ }
+
+ return 0;
+}
+
+void DsTravel::flush(shared_ptr<LocalPlayer> player)
+{
+ int iPad = player->GetXboxPad();
+ for (int i = 0; i < eMethod_MAX; i++)
+ {
+ if (param_cache[iPad][i] > 0)
+ {
+ write( player, (eMethod) i, param_cache[iPad][i] );
+ param_cache[iPad][i] = 0;
+ }
+ }
+}
+
+void DsTravel::write(shared_ptr<LocalPlayer> player, eMethod method, int distance)
+{
+ if (player == nullptr) return;
+
+ app.DebugPrintf("<%ls>\t%s(%i)\n", DurangoStats::getUserId(player), nameMethods[method].c_str(), distance);
+
+ if (method == DsTravel::eMethod_time)
+ {
+ EventWriteIncTimePlayed(
+ DurangoStats::getUserId(player),
+ DurangoStats::getPlayerSession(),
+ player->level->difficulty,
+ distance
+ );
+ }
+ else if ( (eMethod_walk <= method) && (method < eMethod_MAX) )
+ {
+ EventWriteIncDistanceTravelled(
+ DurangoStats::getUserId(player),
+ DurangoStats::getPlayerSession(),
+ player->level->difficulty,
+ distance,
+ method
+ );
+
+ switch(method)
+ {
+ case eMethod_walk:
+ case eMethod_fall:
+ case eMethod_minecart:
+ case eMethod_boat:
+ EventWriteLeaderboardTotals(
+ DurangoStats::getUserId(player), //UserId
+ DurangoStats::getPlayerSession(), // PlayerSessionId
+ player->level->difficulty, // Difficulty,
+ eLeaderboardId_TRAVELLING, // ScoreboardId
+ distance);
+ break;
+ }
+ }
+}
+
+ //////////////////
+ // Ds Item Used //
+ //////////////////
+
+DsItemUsed::DsItemUsed(int id, const wstring &name) : Stat(id,name) {}
+
+void DsItemUsed::handleParamBlob(shared_ptr<LocalPlayer> player, byteArray paramBlob)
+{
+ if (paramBlob.length == sizeof(Param))
+ {
+ Param *param = (Param*) paramBlob.data;
+ app.DebugPrintf("<%ls>\titemUsed(%i,%i,%i)\n", DurangoStats::getUserId(player),
+ param->itemId,
+ param->aux,
+ param->count
+ );
+
+ EventWriteMcItemUsed(
+ DurangoStats::getUserId(player),
+ 0, // SectionId,
+ DurangoStats::getPlayerSession(),
+ 0, // MultiplayerCorrelationId,
+ 0, // Gameplay Mode,
+ player->level->difficulty,
+ param->itemId,
+ 0, 0, 0, // (x,y,z)
+ param->aux,
+ param->count,
+ param->hunger
+ );
+ }
+}
+
+byteArray DsItemUsed::createParamBlob(int itemId, int aux, int count, int health, int hunger)
+{
+ byteArray output;
+ Param param = { itemId, aux, count, health, hunger };
+ output.data = (byte*) new Param(param);
+ output.length = sizeof(Param);
+ return output;
+}
+
+
+ ////////////////////
+ // Ds Achievement //
+ ////////////////////
+
+DsAchievement::DsAchievement(int id, const wstring &name) : Stat(id,name) {}
+
+void DsAchievement::handleParamBlob(shared_ptr<LocalPlayer> player, byteArray paramBlob)
+{
+ if (paramBlob.length == sizeof(SmallParam))
+ {
+ SmallParam *paramS = (SmallParam*) paramBlob.data;
+ assert( DurangoStats::binaryAchievement(paramS->award) );
+ app.DebugPrintf("<%ls>\tAchievement(%i)\n", DurangoStats::getUserId(player), paramS->award);
+
+ bool canAward = true;
+ if(paramS->award == eAward_stayinFrosty)
+ {
+ canAward = !player->m_bHasAwardedStayinFrosty;
+ player->m_bHasAwardedStayinFrosty = true;
+ }
+
+ if(canAward)
+ {
+ EventWriteAchievementGet(
+ DurangoStats::getUserId(player),
+ DurangoStats::getPlayerSession(),
+ paramS->award
+ );
+ }
+ }
+ else if (paramBlob.length == sizeof(LargeParam))
+ {
+ LargeParam *paramL = (LargeParam*) paramBlob.data;
+ assert( DurangoStats::enhancedAchievement(paramL->award) );
+
+ switch(paramL->award)
+ {
+ case eAward_musicToMyEars:
+ app.DebugPrintf("<%ls>\tmusicToMyEars(%i)\n", DurangoStats::getUserId(player), paramL->count);
+ EventWritePlayedMusicDisc(
+ DurangoStats::getUserId(player),
+ DurangoStats::getPlayerSession(),
+ paramL->count
+ );
+ break;
+
+ case eAward_chestfulOfCobblestone:
+ app.DebugPrintf("<%ls>\tchestfulOfCobblestone(%i)\n", DurangoStats::getUserId(player), paramL->count);
+ EventWriteChestfulOfCobblestone(
+ DurangoStats::getUserId(player),
+ DurangoStats::getPlayerSession(),
+ paramL->count
+ );
+ break;
+
+ case eAward_overkill:
+ app.DebugPrintf("<%ls>\toverkill(%i)\n", DurangoStats::getUserId(player), paramL->count);
+ EventWriteOverkill(
+ DurangoStats::getUserId(player),
+ DurangoStats::getPlayerSession(),
+ paramL->count
+ );
+ break;
+
+ case eAward_OnARail:
+ app.DebugPrintf("<%ls>\nonARail(%i)\n", DurangoStats::getUserId(player), paramL->count);
+ EventWriteOnARail(
+ DurangoStats::getUserId(player),
+ DurangoStats::getPlayerSession(),
+ paramL->count
+ );
+ break;
+ }
+ }
+ else assert(false); // Unsuitable paramBlob length.
+}
+
+byteArray DsAchievement::createSmallParamBlob(eAward award)
+{
+ byteArray output;
+ SmallParam param = { award };
+ output.data = (byte*) new SmallParam(param);
+ output.length = sizeof(SmallParam);
+ return output;
+}
+
+byteArray DsAchievement::createLargeParamBlob(eAward award, int count)
+{
+ byteArray output;
+ LargeParam param = { award, count };
+ output.data = (byte*) new LargeParam(param);
+ output.length = sizeof(LargeParam);
+ return output;
+}
+
+
+ //////////////////////////
+ // Ds Changed Dimension //
+ //////////////////////////
+
+DsChangedDimension::DsChangedDimension(int id, const wstring &name) : Stat(id,name) {}
+
+void DsChangedDimension::handleParamBlob(shared_ptr<LocalPlayer> player, byteArray paramBlob)
+{
+ if (paramBlob.length == sizeof(Param))
+ {
+ Param *param = (Param*) paramBlob.data;
+ app.DebugPrintf("<%ls>\tchangedDimension(%i:%i)\n", DurangoStats::getUserId(player),
+ param->fromDimId, param->toDimId);
+
+ // No longer used.
+ }
+}
+
+byteArray DsChangedDimension::createParamBlob(int fromDimId, int toDimId)
+{
+ byteArray output;
+ Param param = { fromDimId, toDimId };
+ output.data = (byte*) new Param(param);
+ output.length = sizeof(Param);
+ return output;
+}
+
+
+ //////////////////////
+ // Ds Entered Biome //
+ //////////////////////
+
+DsEnteredBiome::DsEnteredBiome(int id, const wstring &name) : Stat(id,name) {}
+
+void DsEnteredBiome::handleParamBlob(shared_ptr<LocalPlayer> player, byteArray paramBlob)
+{
+ if (paramBlob.length == sizeof(Param))
+ {
+ Param *param = (Param*) paramBlob.data;
+ app.DebugPrintf("<%ls>\tenteredBiome(%i)\n", DurangoStats::getUserId(player), param->biomeId);
+
+ EventWriteEnteredNewBiome(
+ DurangoStats::getUserId(player),
+ DurangoStats::getPlayerSession(),
+ param->biomeId
+ );
+ }
+}
+
+byteArray DsEnteredBiome::createParamBlob(int biomeId)
+{
+ byteArray output;
+ Param param = { biomeId };
+ output.data = (byte*) new Param(param);
+ output.length = sizeof(Param);
+ return output;
+}
+
+ ////////////////////////
+ // DURANGO STATISTICS //
+ ////////////////////////
+
+DurangoStats::DurangoStats()
+{
+ // Hopefully only using the first parameter
+ itemsAcquired = new DsItemEvent( itemsAcquired_Id, L"itemsAcquired" );
+ itemsAcquired->postConstruct();
+
+ itemUsed = new DsItemUsed( itemUsed_Id, L"itemUsed" );
+ itemUsed->postConstruct();
+
+ travel = new DsTravel( travel_Id, L"travel" );
+ travel->setAwardLocallyOnly()->postConstruct();
+
+ mobKilled = new DsMobKilled( mobKilled_Id, L"mobKilled" );
+ mobKilled->postConstruct();
+
+ mobInteract = new DsMobInteract( mobInteract_Id, L"mobInteract" );
+ mobInteract->postConstruct();
+
+ changedDimension = new DsChangedDimension( changedDimension_Id, L"changedDimension" );
+ changedDimension->postConstruct();
+
+ enteredBiome = new DsEnteredBiome( enteredBiome_Id, L"enteredBiome" );
+ enteredBiome->postConstruct();
+
+ achievement = new DsAchievement( binAchievement_Id, L"achievement" );
+ achievement->postConstruct();
+
+ achievementLocal = new DsAchievement( binAchievementLocal_Id, L"achievementLocal" );
+ achievementLocal->setAwardLocallyOnly();
+ achievementLocal->postConstruct();
+
+ EventRegisterXBLA_149E11AE();
+}
+
+DurangoStats::~DurangoStats()
+{
+ EventUnregisterXBLA_149E11AE();
+}
+
+Stat *DurangoStats::get_stat(int i)
+{
+ switch (i)
+ {
+ case itemsAcquired_Id: return (Stat*) itemsAcquired;
+ case itemUsed_Id: return (Stat*) itemUsed;
+ case travel_Id: return (Stat*) travel;
+ case mobKilled_Id: return (Stat*) mobKilled;
+ case mobInteract_Id: return (Stat*) mobInteract;
+ case changedDimension_Id: return (Stat*) changedDimension;
+ case enteredBiome_Id: return (Stat*) enteredBiome;
+ case binAchievement_Id: return (Stat*) achievement;
+ case binAchievementLocal_Id: return (Stat*) achievementLocal;
+
+ // Unrecognised stat id
+ default: assert(false); break;
+ }
+
+ return NULL;
+}
+
+Stat* DurangoStats::get_walkOneM()
+{
+ return travel;
+}
+
+Stat* DurangoStats::get_swimOneM()
+{
+ return travel;
+}
+
+Stat* DurangoStats::get_fallOneM()
+{
+ return travel;
+}
+
+Stat* DurangoStats::get_climbOneM()
+{
+ return travel;
+}
+
+Stat* DurangoStats::get_minecartOneM()
+{
+ return travel;
+}
+
+Stat* DurangoStats::get_boatOneM()
+{
+ return travel;
+}
+
+Stat* DurangoStats::get_pigOneM()
+{
+ return travel;
+}
+
+Stat *DurangoStats::get_cowsMilked()
+{
+ return get_itemsCrafted(Item::milk_Id);
+}
+
+Stat* DurangoStats::get_killMob()
+{
+ return mobKilled;
+}
+
+Stat* DurangoStats::get_breedEntity(eINSTANCEOF entityId)
+{
+ return mobInteract;
+}
+
+Stat* DurangoStats::get_tamedEntity(eINSTANCEOF entityId)
+{
+ return mobInteract;
+}
+
+Stat* DurangoStats::get_curedEntity(eINSTANCEOF entityId)
+{
+ return mobInteract;
+}
+
+Stat* DurangoStats::get_craftedEntity(eINSTANCEOF entityId)
+{
+ return mobInteract;
+}
+
+Stat* DurangoStats::get_shearedEntity(eINSTANCEOF entityId)
+{
+ return mobInteract;
+}
+
+Stat* DurangoStats::get_timePlayed()
+{
+ return travel;
+}
+
+Stat* DurangoStats::get_blocksPlaced(int blockId)
+{
+ return (Stat*) itemsAcquired;
+}
+
+Stat* DurangoStats::get_blocksMined(int blockId)
+{
+ return (Stat*) itemsAcquired;
+}
+
+Stat* DurangoStats::get_itemsCollected(int itemId, int itemAux)
+{
+ return (Stat*) itemsAcquired;
+}
+
+Stat* DurangoStats::get_itemsCrafted(int itemId)
+{
+ switch (itemId)
+ {
+ // 4J-JEV: These items can be crafted trivially to and from their block equivalents,
+ // 'Acquire Hardware' also relies on 'Count_Crafted(IronIngot) == Count_Forged(IronIngot)" on the Stats server.
+ case Item::ironIngot_Id:
+ case Item::goldIngot_Id:
+ case Item::diamond_Id:
+ case Item::redStone_Id:
+ case Item::emerald_Id:
+ return NULL;
+
+ case Item::dye_powder_Id:
+ default:
+ return (Stat*) itemsAcquired;
+ }
+
+}
+
+Stat* DurangoStats::get_itemsSmelted(int itemId)
+{
+ // 4J-JEV: Context needed for itemCrafted for iron/gold being smelted.
+ return (Stat*) itemsAcquired;
+}
+
+Stat* DurangoStats::get_itemsUsed(int itemId)
+{
+ return (Stat*) itemUsed;
+}
+
+Stat *DurangoStats::get_itemsBought(int itemId)
+{
+ return (Stat*) itemsAcquired;
+}
+
+Stat* DurangoStats::get_changedDimension(int from, int to)
+{
+ return (Stat*) changedDimension;
+}
+
+Stat* DurangoStats::get_enteredBiome(int biomeId)
+{
+ return (Stat*) enteredBiome;
+}
+
+Stat* DurangoStats::get_achievement(eAward achievementId)
+{
+ // Special case for 'binary' achievements.
+ if ( binaryAchievement(achievementId)
+ || enhancedAchievement(achievementId) )
+ {
+ switch (achievementId)
+ {
+ case eAward_chestfulOfCobblestone:
+ case eAward_TakingInventory:
+ return achievementLocal;
+
+ default:
+ return achievement;
+ }
+ }
+ else if (achievementId == eAward_zombieDoctor)
+ {
+ return get_curedEntity(eTYPE_ZOMBIE);
+ }
+
+ // Other achievements awarded through more detailed generic events.
+ return NULL;
+}
+
+byteArray DurangoStats::getParam_walkOneM(int distance)
+{
+ return DsTravel::createParamBlob(DsTravel::eMethod_walk, distance);
+}
+
+byteArray DurangoStats::getParam_swimOneM(int distance)
+{
+ return DsTravel::createParamBlob(DsTravel::eMethod_swim,distance);
+}
+
+byteArray DurangoStats::getParam_fallOneM(int distance)
+{
+ return DsTravel::createParamBlob(DsTravel::eMethod_fall,distance);
+}
+
+byteArray DurangoStats::getParam_climbOneM(int distance)
+{
+ return DsTravel::createParamBlob(DsTravel::eMethod_climb,distance);
+}
+
+byteArray DurangoStats::getParam_minecartOneM(int distance)
+{
+ return DsTravel::createParamBlob(DsTravel::eMethod_minecart,distance);
+}
+
+byteArray DurangoStats::getParam_boatOneM(int distance)
+{
+ return DsTravel::createParamBlob(DsTravel::eMethod_boat,distance);
+}
+
+byteArray DurangoStats::getParam_pigOneM(int distance)
+{
+ return DsTravel::createParamBlob(DsTravel::eMethod_pig,distance);
+}
+
+byteArray DurangoStats::getParam_cowsMilked()
+{
+ return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Crafted, Item::milk_Id, 0, 1);
+}
+
+byteArray DurangoStats::getParam_blocksPlaced(int blockId, int data, int count)
+{
+ return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Placed, blockId, data, count);
+}
+
+byteArray DurangoStats::getParam_blocksMined(int blockId, int data, int count)
+{
+ return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Mined, blockId, data, count);
+}
+
+byteArray DurangoStats::getParam_itemsCollected(int id, int aux, int count)
+{
+ return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Pickedup, id, aux, count);
+}
+
+byteArray DurangoStats::getParam_itemsCrafted(int id, int aux, int count)
+{
+ return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Crafted, id, aux, count);
+}
+
+byteArray DurangoStats::getParam_itemsUsed(shared_ptr<Player> player, shared_ptr<ItemInstance> itm)
+{
+ return DsItemUsed::createParamBlob(
+ itm->getItem()->id, itm->getAuxValue(), itm->GetCount(),
+ player->getHealth(), player->getFoodData()->getFoodLevel()
+ );
+}
+
+byteArray DurangoStats::getParam_itemsBought(int id, int aux, int count)
+{
+ return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Bought, id, aux, count);
+}
+
+byteArray DurangoStats::getParam_mobKill(shared_ptr<Player> player, shared_ptr<Mob> mob, DamageSource *dmgSrc)
+{
+ return DsMobKilled::createParamBlob(player,mob,dmgSrc);
+}
+
+byteArray DurangoStats::getParam_breedEntity(eINSTANCEOF entityId)
+{
+ return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Breed, entityId);
+}
+
+byteArray DurangoStats::getParam_tamedEntity(eINSTANCEOF entityId)
+{
+ return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Tamed, entityId);
+}
+
+byteArray DurangoStats::getParam_curedEntity(eINSTANCEOF entityId)
+{
+ return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Cured, entityId);
+}
+
+byteArray DurangoStats::getParam_craftedEntity(eINSTANCEOF entityId)
+{
+ return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Crafted, entityId);
+}
+
+byteArray DurangoStats::getParam_shearedEntity(eINSTANCEOF entityId)
+{
+ return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Sheared, entityId);
+}
+
+byteArray DurangoStats::getParam_time(int timediff)
+{
+ return DsTravel::createParamBlob(DsTravel::eMethod_time, timediff);
+}
+
+byteArray DurangoStats::getParam_changedDimension(int from, int to)
+{
+ return DsChangedDimension::createParamBlob(from,to);
+}
+
+byteArray DurangoStats::getParam_enteredBiome(int biomeId)
+{
+ return DsEnteredBiome::createParamBlob(biomeId);
+}
+
+byteArray DurangoStats::getParam_achievement(eAward id)
+{
+ if (binaryAchievement(id))
+ {
+ return DsAchievement::createSmallParamBlob(id);
+ }
+ else if (enhancedAchievement(id))
+ {
+ assert(false); // Should be calling the appropriate getParam function.
+ }
+ else if (id == eAward_zombieDoctor)
+ {
+ return getParam_curedEntity(eTYPE_ZOMBIE);
+ }
+
+ // If its not a binary achievement,
+ // don't bother constructing the param blob.
+ return getParam_noArgs();
+}
+
+byteArray DurangoStats::getParam_onARail(int dist)
+{
+ return DsAchievement::createLargeParamBlob(eAward_OnARail, dist);
+}
+
+byteArray DurangoStats::getParam_chestfulOfCobblestone(int count)
+{
+ return DsAchievement::createLargeParamBlob(eAward_chestfulOfCobblestone, count);
+}
+
+byteArray DurangoStats::getParam_overkill(int dmg)
+{
+ return DsAchievement::createLargeParamBlob(eAward_overkill, dmg);
+}
+
+byteArray DurangoStats::getParam_musicToMyEars(int recordId)
+{
+ return DsAchievement::createLargeParamBlob(eAward_musicToMyEars, recordId);
+}
+
+bool DurangoStats::binaryAchievement(eAward achievementId)
+{
+ switch (achievementId)
+ {
+ case eAward_InToTheNether:
+ case eAward_theEnd:
+ case eAward_WhenPigsFly:
+ case eAward_diamondsToYou:
+ case eAward_stayinFrosty:
+ case eAward_renewableEnergy:
+ case eAward_ironMan:
+ case eAward_winGame:
+ case /*maybe*/ eAward_TakingInventory:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/** 4J-JEV,
+ Basically achievements with an inconsequential extra parameter
+ that I thought best not to / prefered not to / couldn't be bothered to make class handlers for.
+ (Motivation: it would be nice for players to see how close they were/are to achieving these things).
+*/
+bool DurangoStats::enhancedAchievement(eAward achievementId)
+{
+ switch (achievementId)
+ {
+ //case eAward_TakingInventory:
+ case eAward_musicToMyEars:
+ case eAward_chestfulOfCobblestone:
+ case eAward_overkill:
+ case eAward_OnARail:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+void DurangoStats::generatePlayerSession()
+{
+ DurangoStats *dsInstance = (DurangoStats *) GenericStats::getInstance();
+ CoCreateGuid( &dsInstance->playerSessionId );
+}
+
+LPCGUID DurangoStats::getPlayerSession()
+{
+ DurangoStats *dsInstance = (DurangoStats *) GenericStats::getInstance();
+ LPCGUID lpcguid = &dsInstance->playerSessionId;
+ return lpcguid;
+}
+
+void DurangoStats::setMultiplayerCorrelationId(Platform::String^ mcpId)
+{
+ ((DurangoStats*)GenericStats::getInstance())->multiplayerCorrelationId = mcpId;
+}
+
+LPCWSTR DurangoStats::getMultiplayerCorrelationId()
+{
+ return ((DurangoStats*)GenericStats::getInstance())->multiplayerCorrelationId->Data();
+}
+
+LPCWSTR DurangoStats::getUserId(shared_ptr<LocalPlayer> player)
+{
+ return getUserId(player->GetXboxPad());
+}
+
+LPCWSTR DurangoStats::getUserId(int iPad)
+{
+ static wstring cache = L"";
+ PlayerUID uid = INVALID_XUID;
+ ProfileManager.GetXUID(iPad, &uid, true);
+ cache = uid.toString();
+ return cache.c_str();
+}
+
+void DurangoStats::playerSessionStart(PlayerUID uid, shared_ptr<Player> plr)
+{
+ if (plr != NULL && plr->level != NULL && plr->level->getLevelData() != NULL)
+ {
+ //wprintf(uid.toString().c_str());
+
+ //EventWritePlayerSessionStart(
+ app.DebugPrintf(">>>\tPlayerSessionStart(%ls,%s,%ls,%i,%i)\n",
+ uid.toString(),
+ DurangoStats::getPlayerSession(),
+ DurangoStats::getMultiplayerCorrelationId(),
+ plr->level->getLevelData()->getGameType()->isSurvival(),
+ plr->level->difficulty
+ );
+
+ EventWritePlayerSessionStart(
+ uid.toString().c_str(),
+ DurangoStats::getPlayerSession(),
+ DurangoStats::getMultiplayerCorrelationId(),
+ plr->level->getLevelData()->getGameType()->isSurvival(),
+ plr->level->difficulty
+ );
+ }
+}
+
+void DurangoStats::playerSessionStart(int iPad)
+{
+ PlayerUID puid; shared_ptr<Player> plr;
+ ProfileManager.GetXUID(iPad, &puid, true);
+ plr = Minecraft::GetInstance()->localplayers[iPad];
+ playerSessionStart(puid,plr);
+}
+
+void DurangoStats::playerSessionPause(int iPad)
+{
+ shared_ptr<MultiplayerLocalPlayer> plr = Minecraft::GetInstance()->localplayers[iPad];
+ if (plr != NULL && plr->level != NULL && plr->level->getLevelData() != NULL)
+ {
+ PlayerUID puid;
+ ProfileManager.GetXUID(iPad, &puid, true);
+
+ //EventWritePlayerSessionPause(
+ app.DebugPrintf(">>>\tPlayerSessionPause(%ls,%s,%ls)\n",
+ puid.toString().c_str(),
+ DurangoStats::getPlayerSession(),
+ DurangoStats::getMultiplayerCorrelationId()
+ );
+
+ EventWritePlayerSessionPause(
+ puid.toString().c_str(),
+ DurangoStats::getPlayerSession(),
+ DurangoStats::getMultiplayerCorrelationId()
+ );
+ }
+}
+
+void DurangoStats::playerSessionResume(int iPad)
+{
+ shared_ptr<MultiplayerLocalPlayer> plr = Minecraft::GetInstance()->localplayers[iPad];
+ if (plr != NULL && plr->level != NULL && plr->level->getLevelData() != NULL)
+ {
+ PlayerUID puid;
+ ProfileManager.GetXUID(iPad, &puid, true);
+
+ //EventWritePlayerSessionResume(
+ app.DebugPrintf(">>>\tPlayerSessionResume(%ls,%s,%ls,%i,%i)\n",
+ puid.toString().c_str(),
+ DurangoStats::getPlayerSession(),
+ DurangoStats::getMultiplayerCorrelationId(),
+ plr->level->getLevelData()->getGameType()->isSurvival(),
+ plr->level->difficulty
+ );
+
+ EventWritePlayerSessionResume(
+ puid.toString().c_str(),
+ DurangoStats::getPlayerSession(),
+ DurangoStats::getMultiplayerCorrelationId(),
+ plr->level->getLevelData()->getGameType()->isSurvival(),
+ plr->level->difficulty
+ );
+ }
+}
+
+void DurangoStats::playerSessionEnd(int iPad)
+{
+ shared_ptr<MultiplayerLocalPlayer> plr = Minecraft::GetInstance()->localplayers[iPad];
+ if (plr != NULL)
+ {
+ DurangoStats::getInstance()->travel->flush(plr);
+ }
+} \ No newline at end of file