mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-07-16 08:55:18 +00:00
early reivision for v010; recording ongoing shots fired and landed
This commit is contained in:
parent
ac0892ab34
commit
c869cdc8a0
16 changed files with 736 additions and 95 deletions
335
server/src/main/resources/db/migration/V010__Scoring2.sql
Normal file
335
server/src/main/resources/db/migration/V010__Scoring2.sql
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
/* changes to objects from V008__Scoring.sql */
|
||||
ALTER TABLE killactivity
|
||||
ADD COLUMN victim_mounted INT NO NULL DEFAULT 0;
|
||||
|
||||
DROP PROCEDURE IF EXISTS proc_sessionnumber_initAndOrIncrease;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE proc_sessionnumber_initAndOrIncreasePerHour
|
||||
(avatarId IN Int, number OUT Int, nextNumber OUT Int)
|
||||
AS
|
||||
$$
|
||||
DECLARE time TIMESTAMP;
|
||||
BEGIN
|
||||
SELECT MAX(session_id) INTO number
|
||||
FROM sessionnumber
|
||||
WHERE avatar_id = avatarId;
|
||||
SELECT COALESCE(timestamp) INTO time
|
||||
FROM sessionnumber
|
||||
WHERE avatar_id = avatarId AND session_id = number;
|
||||
IF (time IS null) THEN
|
||||
number := 0;
|
||||
nextNumber := 1;
|
||||
INSERT INTO sessionnumber
|
||||
VALUES (avatarId, nextNumber);
|
||||
ELSIF (CURRENT_TIMESTAMP > DATE_TRUNC('hour', time) + interval '1' hour) THEN
|
||||
nextNumber := number + 1;
|
||||
INSERT INTO sessionnumber
|
||||
VALUES (avatarId, nextNumber);
|
||||
ELSE
|
||||
nextNumber := number;
|
||||
UPDATE sessionnumber
|
||||
SET timestamp = CURRENT_TIMESTAMP
|
||||
WHERE avatar_id = avatarId AND session_id = number;
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE proc_sessionnumber_get
|
||||
(avatarId IN Int, number OUT Int)
|
||||
AS
|
||||
$$
|
||||
DECLARE sessionId Int;
|
||||
BEGIN
|
||||
SELECT COALESCE(session_id) INTO sessionId
|
||||
FROM account
|
||||
WHERE avatar_logged_in = avatarId;
|
||||
IF sessionId IS NULL THEN
|
||||
number := 0;
|
||||
ELSE
|
||||
number := sessionId;
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE proc_weaponstat_addEntryIfNone
|
||||
(avatarId IN Int, weaponId IN Int)
|
||||
AS
|
||||
$$
|
||||
BEGIN
|
||||
IF NOT EXISTS(
|
||||
SELECT avatar_id, weapon_id
|
||||
FROM weaponstat
|
||||
WHERE avatar_id = avatarId AND weapon_id = weaponId
|
||||
GROUP BY avatar_id, weapon_id
|
||||
) THEN
|
||||
INSERT INTO weaponstat (avatar_id, weapon_id)
|
||||
VALUES (avatarId, weaponId);
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE proc_weaponstatsession_addEntryIfNoneWithSessionId
|
||||
(avatarId IN Int, sessionId IN Int, weaponId IN Int)
|
||||
AS
|
||||
$$
|
||||
BEGIN
|
||||
IF NOT EXISTS(
|
||||
SELECT avatar_id, session_id, weapon_id
|
||||
FROM weaponstatsession
|
||||
WHERE avatar_id = avatarId AND weapon_id = weaponId AND session_id = sessionId
|
||||
GROUP BY avatar_id, session_id, weapon_id
|
||||
) THEN
|
||||
INSERT INTO weaponstatsession (avatar_id, session_id, weapon_id)
|
||||
VALUES (avatarId, sessionId, weaponId);
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE proc_weaponstatsession_addEntryIfNone
|
||||
(avatarId IN Int, weaponId IN Int)
|
||||
AS
|
||||
$$
|
||||
DECLARE sessionId Int;
|
||||
BEGIN
|
||||
CALL proc_sessionnumber_get(avatarId, sessionId);
|
||||
CALL proc_weaponstatsession_addEntryIfNoneWithSessionId(avatarId, sessionId, weaponId);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION fn_account_newSession()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$$
|
||||
DECLARE avatarId Int;
|
||||
DECLARE sessionId Int;
|
||||
DECLARE oldSessionId Int;
|
||||
BEGIN
|
||||
avatarId := NEW.avatar_logged_in;
|
||||
CALL proc_sessionnumber_initAndOrIncreasePerHour(avatarId, oldSessionId, sessionId);
|
||||
IF (sessionId > oldSessionId) THEN
|
||||
BEGIN
|
||||
UPDATE account
|
||||
SET session_id = sessionId
|
||||
WHERE id = OLD.id;
|
||||
INSERT INTO kdasession (avatar_id, session_id)
|
||||
VALUES (avatarId, sessionId);
|
||||
END;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE TRIGGER psf_account_newSession
|
||||
AFTER UPDATE
|
||||
OF avatar_logged_in
|
||||
ON account
|
||||
FOR EACH ROW
|
||||
WHEN (OLD.avatar_logged_in = 0 AND NEW.avatar_logged_in > 0)
|
||||
EXECUTE FUNCTION fn_account_newSession();
|
||||
|
||||
CREATE OR REPLACE FUNCTION fn_killactivity_updateRelatedStats()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$$
|
||||
DECLARE killerSessionId Int;
|
||||
DECLARE victimSessionId Int;
|
||||
DECLARE killerId Int;
|
||||
DECLARE victimId Int;
|
||||
DECLARE weaponId Int;
|
||||
BEGIN
|
||||
killerId := NEW.killer_id;
|
||||
victimId := NEW.victim_id;
|
||||
weaponId := NEW.weapon_id;
|
||||
CALL proc_sessionnumber_get(killerId, killerSessionId);
|
||||
CALL proc_sessionnumber_get(victimId, victimSessionId);
|
||||
BEGIN
|
||||
UPDATE kdasession
|
||||
SET kills = kills + 1
|
||||
WHERE avatar_id = killerId AND session_id = killerSessionId;
|
||||
END;
|
||||
BEGIN
|
||||
UPDATE kdasession
|
||||
SET deaths = deaths + 1
|
||||
WHERE avatar_id = victimId AND session_id = victimSessionId;
|
||||
END;
|
||||
BEGIN
|
||||
CALL proc_weaponstatsession_addEntryIfNoneWithSessionId(killerId, killerSessionId, weaponId);
|
||||
UPDATE weaponstatsession
|
||||
SET kills = kills + 1
|
||||
WHERE avatar_id = killerId AND session_id = killerSessionId AND weapon_id = weaponId;
|
||||
END;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION fn_weaponstatsession_addEntryIfNone()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$$
|
||||
DECLARE avatarId Int;
|
||||
DECLARE weaponId Int;
|
||||
BEGIN
|
||||
avatarId := NEW.avatar_id;
|
||||
weaponId := NEW.weapon_id;
|
||||
CALL proc_weaponstatsession_addEntryIfNone(avatarId, weaponId);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION fn_kda_addEntryIfNone()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$$
|
||||
DECLARE avatarId Int;
|
||||
BEGIN
|
||||
avatarId := NEW.avatar_id;
|
||||
CALL proc_kda_addEntryIfNone(avatarId);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION fn_weaponstat_addEntryIfNone()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$$
|
||||
DECLARE avatarId Int;
|
||||
DECLARE weaponId Int;
|
||||
BEGIN
|
||||
avatarId := NEW.avatar_id;
|
||||
weaponId := NEW.weapon_id;
|
||||
CALL proc_weaponstat_addEntryIfNone(avatarId, weaponId);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION fn_kdasession_updateOnDelete()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$$
|
||||
DECLARE avatarId Int;
|
||||
DECLARE oldKills Int;
|
||||
DECLARE oldDeaths Int;
|
||||
DECLARE oldAssists Int;
|
||||
BEGIN
|
||||
avatarId := OLD.avatar_id;
|
||||
oldKills := OLD.kills;
|
||||
oldDeaths := OLD.deaths;
|
||||
oldAssists := OLD.assists;
|
||||
CALL proc_kda_addEntryIfNone(avatarId);
|
||||
UPDATE kda
|
||||
SET kills = kills + oldKills,
|
||||
deaths = deaths + oldDeaths,
|
||||
assists = assists + oldAssists
|
||||
WHERE avatar_id = avatarId;
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION fn_weaponstatsession_updateOnDelete()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$$
|
||||
DECLARE avatarId Int;
|
||||
DECLARE weaponId Int;
|
||||
DECLARE oldKills Int;
|
||||
DECLARE oldAssists Int;
|
||||
DECLARE oldFired Int;
|
||||
DECLARE oldLanded Int;
|
||||
BEGIN
|
||||
avatarId := OLD.avatar_id;
|
||||
weaponId := OLD.weapon_id;
|
||||
oldKills := OLD.kills;
|
||||
oldAssists := OLD.assists;
|
||||
oldFired := OLD.shots_fired;
|
||||
oldLanded := OLD.shots_landed;
|
||||
CALL proc_weaponstat_addEntryIfNone(avatarId, weaponId);
|
||||
UPDATE weaponstat
|
||||
SET kills = kills + oldKills,
|
||||
assists = assists + oldAssists,
|
||||
shots_fired = shots_fired + oldFired,
|
||||
shots_landed = shots_landed + oldLanded
|
||||
WHERE avatar_id = avatarId AND weapon_id = weaponId;
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
/* new objects */
|
||||
CREATE OR REPLACE TABLE machinedestroyed (
|
||||
"index" SERIAL PRIMARY KEY NOT NULL,
|
||||
"avatar_id" INT NOT NULL REFERENCES avatar (id),
|
||||
"weapon_id" INT NOT NULL,
|
||||
"machine_type" INT NOT NULL,
|
||||
"machine_faction" SMALLINT NOT NULL,
|
||||
"hacked_faction" SMALLINT NOT NULL,
|
||||
"as_cargo" BOOLEAN NOT NULL,
|
||||
"zone_num" SMALLINT NOT NULL,
|
||||
"px" INT NOT NULL,
|
||||
"py" INT NOT NULL,
|
||||
"pz" INT NOT NULL,
|
||||
"timestamp" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
/*
|
||||
expectations:
|
||||
heal player with medapp: no intermediate_type, implement_type is the medapp (no, yes)
|
||||
revive player with medapp: intermediate_type is 121, implement_type is the medapp (yes, yes)
|
||||
repair max with bank: no intermediate_type, implement_type is the bank (no, yes)
|
||||
repair foo with gluegun: intermediate_type is the foo object type, implement_type is the gluegun (yes, yes)
|
||||
bail from vehicle: intermediate_type is the vehicle object type, no implement_type (yes, no)
|
||||
cargo from vehicle: intermediate_type is the vehicle object type, implement_type is the cargo vehicle object type (yes, yes)
|
||||
damage from vehicle (collision): no intermediate_type, implement_type is vehicle object type (no, yes)
|
||||
damage from vehicle (explosion): intermediate_type is vehicle object type, implement_type is intermediate_type (yes, yes)
|
||||
always ignore target_id = user_id
|
||||
*/
|
||||
CREATE OR REPLACE TABLE supportactivity (
|
||||
"index" SERIAL PRIMARY KEY NOT NULL,
|
||||
"user_id" INT NOT NULL REFERENCES avatar (id), -- player that provides the support
|
||||
"target_id" INT NOT NULL REFERENCES avatar (id), -- benefactor of the support
|
||||
"interaction_type" SMALLINT NOT NULL, -- description of support
|
||||
"intermediate_type" INT DEFAULT 0, -- through what medium user_id supports target_id
|
||||
"implement_type" INT DEFAULT 0, -- tool utilized by user_id to support target_id, potentially via interaction with intermediate_type
|
||||
"bep" INT NOT NULL,
|
||||
"timestamp" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
/*
|
||||
Before inserting a value into the weaponstatsession table to session id -1,
|
||||
correct the session id to the most current session id,
|
||||
and invalidate the attempted insertion at that -1 session id.
|
||||
*/
|
||||
CREATE OR REPLACE FUNCTION fn_weaponstatsession_beforeInsert()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$$
|
||||
DECLARE avatarId Int;
|
||||
DECLARE weaponId Int;
|
||||
DECLARE sessionId Int;
|
||||
DECLARE oldAssists Int;
|
||||
DECLARE oldFired Int;
|
||||
DECLARE oldLanded Int;
|
||||
BEGIN
|
||||
avatarId := NEW.avatar_id;
|
||||
weaponId := NEW.weapon_id;
|
||||
oldAssists := NEW.assists;
|
||||
oldFired := NEW.shots_fired;
|
||||
oldLanded := NEW.shots_landed;
|
||||
CALL proc_sessionnumber_get(avatarId, sessionId);
|
||||
CALL proc_weaponstatsession_addEntryIfNoneWithSessionId(avatarId, sessionId, weaponId);
|
||||
BEGIN
|
||||
UPDATE weaponstatsession
|
||||
SET
|
||||
assists = assists + oldAssists,
|
||||
shots_fired = shots_fired + oldFired,
|
||||
shots_landed = shots_landed + oldLanded
|
||||
WHERE avatar_id = avatarId AND session_id = sessionId AND weapon_id = weaponId;
|
||||
END;
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE TRIGGER psf_weaponstatsession_beforeInsert
|
||||
BEFORE INSERT
|
||||
ON weaponstatsession
|
||||
FOR EACH ROW
|
||||
WHEN (NEW.session_id = -1)
|
||||
EXECUTE FUNCTION fn_weaponstatsession_beforeInsert();
|
||||
|
|
@ -137,12 +137,10 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
|
|||
bcryptedPassword
|
||||
}
|
||||
|
||||
def getAccountLogin(username: String, password: Option[String], token: Option[String]): Unit = {
|
||||
|
||||
if (token.isDefined) {
|
||||
accountLoginWithToken(token.getOrElse(""))
|
||||
} else {
|
||||
accountLogin(username, password.getOrElse(""))
|
||||
def getAccountLogin(username: String, passwordOpt: Option[String], tokenOpt: Option[String]): Unit = {
|
||||
tokenOpt match {
|
||||
case Some(token) => accountLoginWithToken(token)
|
||||
case None => accountLogin(username, passwordOpt.getOrElse(""))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +162,8 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
|
|||
accountOption <- accountsExact.headOption orElse accountsLower.headOption match {
|
||||
|
||||
// account found
|
||||
case Some(account) => Future.successful(Some(account))
|
||||
case Some(account) =>
|
||||
Future.successful(Some(account))
|
||||
|
||||
// create new account
|
||||
case None =>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import java.util.concurrent.atomic.AtomicInteger
|
|||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects.avatar.scoring.{Assist, Death, EquipmentStat, KDAStat, Kill}
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.sourcing.VehicleSource
|
||||
import net.psforever.objects.vital.InGameHistory
|
||||
import net.psforever.objects.vehicles.MountedWeapons
|
||||
import org.joda.time.{LocalDateTime, Seconds}
|
||||
|
|
@ -840,6 +841,15 @@ object AvatarActor {
|
|||
out.future
|
||||
}
|
||||
|
||||
def avatarNoLongerLoggedIn(accountId: Long): Unit = {
|
||||
import ctx._
|
||||
ctx.run(
|
||||
query[persistence.Account]
|
||||
.filter(_.id == lift(accountId))
|
||||
.update(_.avatarLoggedIn -> lift(0L))
|
||||
)
|
||||
}
|
||||
|
||||
def toAvatar(avatar: persistence.Avatar): Avatar = {
|
||||
val bep = avatar.bep
|
||||
val convertedCosmetics = if (BattleRank.showCosmetics(bep)) {
|
||||
|
|
@ -1044,13 +1054,13 @@ class AvatarActor(
|
|||
} yield true
|
||||
inits.onComplete {
|
||||
case Success(_) =>
|
||||
performAvatarLogin(avatarId, account.id, replyTo)
|
||||
performAvatarLoginTest(avatarId, account.id, replyTo)
|
||||
case Failure(e) =>
|
||||
log.error(e)("db failure")
|
||||
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 6))
|
||||
}
|
||||
} else {
|
||||
performAvatarLogin(avatarId, account.id, replyTo)
|
||||
performAvatarLoginTest(avatarId, account.id, replyTo)
|
||||
}
|
||||
case Success(_) =>
|
||||
//TODO this may not be an actual failure, but don't know what to do
|
||||
|
|
@ -1069,6 +1079,11 @@ class AvatarActor(
|
|||
buffer.stash(other)
|
||||
Behaviors.same
|
||||
}
|
||||
.receiveSignal {
|
||||
case (_, PostStop) =>
|
||||
AvatarActor.avatarNoLongerLoggedIn(account.id)
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
||||
def postCharacterSelectBehaviour(): Behavior[Command] = {
|
||||
|
|
@ -1828,6 +1843,7 @@ class AvatarActor(
|
|||
}
|
||||
.receiveSignal {
|
||||
case (_, PostStop) =>
|
||||
AvatarActor.avatarNoLongerLoggedIn(account.get.id)
|
||||
AvatarActor.saveAvatarData(avatar)
|
||||
staminaRegenTimer.cancel()
|
||||
implantTimers.values.foreach(_.cancel())
|
||||
|
|
@ -1848,9 +1864,25 @@ class AvatarActor(
|
|||
Future.failed(ex).asInstanceOf[Future[Loadout]]
|
||||
}
|
||||
|
||||
def performAvatarLoginTest(avatarId: Long, accountId: Long, replyTo: ActorRef[AvatarLoginResponse]): Unit = {
|
||||
import ctx._
|
||||
val blockLogInIfNot = for {
|
||||
out <- ctx.run(query[persistence.Account].filter(_.id == lift(accountId)))
|
||||
} yield out
|
||||
blockLogInIfNot.onComplete {
|
||||
case Success(account)
|
||||
//TODO test against acceptable player factions
|
||||
if account.exists { _.avatarLoggedIn == 0 } =>
|
||||
//accept
|
||||
performAvatarLogin(avatarId, accountId, replyTo)
|
||||
case _ =>
|
||||
//refuse
|
||||
//TODO refuse
|
||||
}
|
||||
}
|
||||
|
||||
def performAvatarLogin(avatarId: Long, accountId: Long, replyTo: ActorRef[AvatarLoginResponse]): Unit = {
|
||||
import ctx._
|
||||
|
||||
val result = for {
|
||||
//log this login
|
||||
_ <- ctx.run(
|
||||
|
|
@ -1862,7 +1894,10 @@ class AvatarActor(
|
|||
_ <- ctx.run(
|
||||
query[persistence.Account]
|
||||
.filter(_.id == lift(accountId))
|
||||
.update(_.lastFactionId -> lift(avatar.faction.id))
|
||||
.update(
|
||||
_.lastFactionId -> lift(avatar.faction.id),
|
||||
_.avatarLoggedIn -> lift(avatarId)
|
||||
)
|
||||
)
|
||||
//retrieve avatar data
|
||||
loadouts <- initializeAllLoadouts()
|
||||
|
|
@ -2995,8 +3030,26 @@ class AvatarActor(
|
|||
player.HistoryAndContributions()
|
||||
}
|
||||
zone.actor ! ZoneActor.RewardOurSupporters(playerSource, historyTranscript, kill, exp)
|
||||
case _: Assist =>
|
||||
()
|
||||
val target = kill.info.targetAfter.asInstanceOf[PlayerSource]
|
||||
val targetMounted = target.seatedIn.map { case (v: VehicleSource, seat) =>
|
||||
val definition = v.Definition
|
||||
definition.ObjectId * 10 + Vehicles.SeatPermissionGroup(definition, seat).map { _.id }.getOrElse(0)
|
||||
}.getOrElse(0)
|
||||
zones.exp.ToDatabase.reportKillBy(
|
||||
avatar.id.toLong,
|
||||
target.CharId,
|
||||
target.ExoSuit.id,
|
||||
targetMounted,
|
||||
kill.info.interaction.cause.attribution,
|
||||
player.Zone.Number,
|
||||
target.Position,
|
||||
kill.experienceEarned
|
||||
)
|
||||
case assist: Assist =>
|
||||
val avatarId = avatar.id.toLong
|
||||
assist.weapons.foreach {
|
||||
zones.exp.ToDatabase.reportAssistKills(avatarId, _, assists = 1)
|
||||
}
|
||||
case _: Death =>
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
player.Name,
|
||||
|
|
@ -3019,6 +3072,7 @@ class AvatarActor(
|
|||
|
||||
def updateToolDischarge(stats: EquipmentStat): Unit = {
|
||||
avatar.scorecard.rate(stats)
|
||||
zones.exp.ToDatabase.reportToolDischarge(avatar.id.toLong, stats)
|
||||
}
|
||||
|
||||
def createAvatar(
|
||||
|
|
|
|||
|
|
@ -542,6 +542,7 @@ class SessionAvatarHandlers(
|
|||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionData.zoning
|
||||
AvatarActor.savePlayerLocation(player)
|
||||
sessionData.shooting.reportOngoingShots(sessionData.shooting.reportOngoingShotsToDatabase)
|
||||
sessionData.zoning.spawn.shiftPosition = Some(player.Position)
|
||||
|
||||
//respawn
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
package net.psforever.actors.session.support
|
||||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.objects.zones.exp.ToDatabase
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
|
|
@ -43,7 +45,8 @@ private[support] class WeaponAndProjectileOperations(
|
|||
var prefire: mutable.Set[PlanetSideGUID] = mutable.Set.empty //if WeaponFireMessage precedes ChangeFireStateMessage_Start
|
||||
private[support] var shootingStart: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
|
||||
private[support] var shootingStop: mutable.HashMap[PlanetSideGUID, Long] = mutable.HashMap[PlanetSideGUID, Long]()
|
||||
private var ongoingShotsFired: Int = 0
|
||||
private val shotsFired: mutable.HashMap[Int,Int] = mutable.HashMap[Int,Int]()
|
||||
private val shotsLanded: mutable.HashMap[Int,Int] = mutable.HashMap[Int,Int]()
|
||||
private[support] var shotsWhileDead: Int = 0
|
||||
private val projectiles: Array[Option[Projectile]] =
|
||||
Array.fill[Option[Projectile]](Projectile.rangeUID - Projectile.baseUID)(None)
|
||||
|
|
@ -157,7 +160,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
fireStateStopPlayerMessages(item_guid)
|
||||
case Some(_) =>
|
||||
fireStateStopMountedMessages(item_guid)
|
||||
case _ => ()
|
||||
case _ =>
|
||||
log.warn(s"ChangeFireState_Stop: can not find $item_guid")
|
||||
}
|
||||
sessionData.progressBarUpdate.cancel()
|
||||
|
|
@ -275,8 +278,8 @@ private[support] class WeaponAndProjectileOperations(
|
|||
val LongRangeProjectileInfoMessage(guid, _, _) = pkt
|
||||
FindContainedWeapon match {
|
||||
case (Some(_: Vehicle), weapons)
|
||||
if weapons.exists { _.GUID == guid } => ; //now what?
|
||||
case _ => ;
|
||||
if weapons.exists { _.GUID == guid } => () //now what?
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -329,13 +332,11 @@ private[support] class WeaponAndProjectileOperations(
|
|||
_: Vector3,
|
||||
hitPos: Vector3
|
||||
) =>
|
||||
ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos) match {
|
||||
case Some(resprojectile) =>
|
||||
avatarActor ! AvatarActor.UpdateToolDischarge(EquipmentStat(resprojectile.cause.attribution,0,1,0))
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
case None => ;
|
||||
ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos).collect { resprojectile =>
|
||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
}
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
case None =>
|
||||
log.warn(s"ResolveProjectile: expected projectile, but ${projectile_guid.guid} not found")
|
||||
|
|
@ -368,26 +369,22 @@ private[support] class WeaponAndProjectileOperations(
|
|||
sessionData.validObject(direct_victim_uid, decorator = "SplashHit/direct_victim") match {
|
||||
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
CheckForHitPositionDiscrepancy(projectile_guid, target.Position, target)
|
||||
ResolveProjectileInteraction(projectile, resolution1, target, target.Position) match {
|
||||
case Some(resprojectile) =>
|
||||
avatarActor ! AvatarActor.UpdateToolDischarge(EquipmentStat(resprojectile.cause.attribution,0,1,0))
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
case None => ;
|
||||
ResolveProjectileInteraction(projectile, resolution1, target, target.Position).collect { resprojectile =>
|
||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
}
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
//other victims
|
||||
targets.foreach(elem => {
|
||||
sessionData.validObject(elem.uid, decorator = "SplashHit/other_victims") match {
|
||||
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target)
|
||||
ResolveProjectileInteraction(projectile, resolution2, target, explosion_pos) match {
|
||||
case Some(resprojectile) =>
|
||||
avatarActor ! AvatarActor.UpdateToolDischarge(EquipmentStat(resprojectile.cause.attribution,0,1,0))
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
case None => ;
|
||||
ResolveProjectileInteraction(projectile, resolution2, target, explosion_pos).collect { resprojectile =>
|
||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
}
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
})
|
||||
//...
|
||||
|
|
@ -413,7 +410,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
continent.Projectile ! ZoneProjectile.Remove(projectile.GUID)
|
||||
}
|
||||
}
|
||||
case None => ;
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -422,13 +419,12 @@ private[support] class WeaponAndProjectileOperations(
|
|||
sessionData.validObject(victim_guid, decorator = "Lash") match {
|
||||
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
CheckForHitPositionDiscrepancy(projectile_guid, hit_pos, target)
|
||||
ResolveProjectileInteraction(projectile_guid, DamageResolution.Lash, target, hit_pos) match {
|
||||
case Some(resprojectile) =>
|
||||
avatarActor ! AvatarActor.UpdateToolDischarge(EquipmentStat(resprojectile.cause.attribution,0,1,0))
|
||||
ResolveProjectileInteraction(projectile_guid, DamageResolution.Lash, target, hit_pos).foreach {
|
||||
resprojectile =>
|
||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
case None => ;
|
||||
}
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -524,7 +520,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
obj match {
|
||||
case turret: FacilityTurret if turret.Definition == GlobalDefinitions.vanu_sentry_turret =>
|
||||
turret.Actor ! FacilityTurret.WeaponDischarged()
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
} else {
|
||||
log.warn(
|
||||
|
|
@ -532,7 +528,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
)
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -552,7 +548,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
case Some(v: Vehicle) =>
|
||||
//assert subsystem states
|
||||
v.SubsystemMessages().foreach { sendResponse }
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
if (enabledTools.nonEmpty) {
|
||||
|
|
@ -577,8 +573,9 @@ private[support] class WeaponAndProjectileOperations(
|
|||
avatarActor ! AvatarActor.ConsumeStamina(avatar.stamina)
|
||||
}
|
||||
avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds)
|
||||
tool.Discharge()
|
||||
prefire += weaponGUID
|
||||
ongoingShotsFired = ongoingShotsFired + tool.Discharge()
|
||||
addShotsFired(tool.Definition.ObjectId, tool.AmmoSlot.Chamber)
|
||||
}
|
||||
(o, Some(tool))
|
||||
}
|
||||
|
|
@ -635,7 +632,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
continent.GUID(weapon_guid) match {
|
||||
case Some(tool: Tool) =>
|
||||
EmptyMagazine(weapon_guid, tool)
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -719,19 +716,17 @@ private[support] class WeaponAndProjectileOperations(
|
|||
*/
|
||||
def ModifyAmmunitionInMountable(obj: PlanetSideServerObject with Container)(box: AmmoBox, reloadValue: Int): Unit = {
|
||||
ModifyAmmunition(obj)(box, reloadValue)
|
||||
obj.Find(box) match {
|
||||
case Some(index) =>
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
s"${obj.Actor}",
|
||||
VehicleAction.InventoryState(
|
||||
player.GUID,
|
||||
box,
|
||||
obj.GUID,
|
||||
index,
|
||||
box.Definition.Packet.DetailedConstructorData(box).get
|
||||
)
|
||||
obj.Find(box).collect { index =>
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
s"${obj.Actor}",
|
||||
VehicleAction.InventoryState(
|
||||
player.GUID,
|
||||
box,
|
||||
obj.GUID,
|
||||
index,
|
||||
box.Definition.Packet.DetailedConstructorData(box).get
|
||||
)
|
||||
case None => ;
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -849,7 +844,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
sessionData.normalItemDrop(player, continent)(previousBox)
|
||||
}
|
||||
AmmoBox.Split(previousBox) match {
|
||||
case Nil | List(_) => ; //done (the former case is technically not possible)
|
||||
case Nil | List(_) => () //done (the former case is technically not possible)
|
||||
case _ :: toUpdate =>
|
||||
modifyFunc(previousBox, 0) //update to changed capacity value
|
||||
toUpdate.foreach(box => { TaskWorkflow.execute(stowNewFunc(box)) })
|
||||
|
|
@ -1151,7 +1146,6 @@ private[support] class WeaponAndProjectileOperations(
|
|||
prefire -= itemGuid
|
||||
shooting += itemGuid
|
||||
shootingStart += itemGuid -> System.currentTimeMillis()
|
||||
ongoingShotsFired = 0
|
||||
}
|
||||
|
||||
private def fireStateStartChargeMode(tool: Tool): Unit = {
|
||||
|
|
@ -1220,11 +1214,10 @@ private[support] class WeaponAndProjectileOperations(
|
|||
used by ChangeFireStateMessage_Stop handling
|
||||
*/
|
||||
private def fireStateStopUpdateChargeAndCleanup(tool: Tool): Unit = {
|
||||
avatarActor ! AvatarActor.UpdateToolDischarge(EquipmentStat(tool.Definition.ObjectId, ongoingShotsFired, 0, 0))
|
||||
tool.FireMode match {
|
||||
case _: ChargeFireModeDefinition =>
|
||||
sendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, tool.Magazine))
|
||||
case _ => ;
|
||||
case _ => ()
|
||||
}
|
||||
if (tool.Magazine == 0) {
|
||||
FireCycleCleanup(tool)
|
||||
|
|
@ -1365,6 +1358,49 @@ private[support] class WeaponAndProjectileOperations(
|
|||
)
|
||||
}
|
||||
|
||||
private def addShotsFired(weaponId: Int, shots: Int): Unit = {
|
||||
addShotsToMap(shotsFired, weaponId, shots)
|
||||
}
|
||||
|
||||
private def addShotsLanded(weaponId: Int, shots: Int): Unit = {
|
||||
addShotsToMap(shotsLanded, weaponId, shots)
|
||||
}
|
||||
|
||||
private def addShotsToMap(map: mutable.HashMap[Int, Int], weaponId: Int, shots: Int): Unit = {
|
||||
map.put(
|
||||
weaponId,
|
||||
map.get(weaponId) match {
|
||||
case Some(previousShots) => previousShots + shots
|
||||
case None => shots
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private[support] def reportOngoingShots(reportFunc: (Long, Int, Int, Int) => Unit): Unit = {
|
||||
reportOngoingShots(player.CharId, reportFunc)
|
||||
}
|
||||
|
||||
private[support] def reportOngoingShots(avatarId: Long, reportFunc: (Long, Int, Int, Int) => Unit): Unit = {
|
||||
//only shots that have been reported as fired count
|
||||
//if somehow shots had reported as landed but never reported as fired, they are ignored
|
||||
//these are just raw counts; there's only numeric connection between the entries of fired and of landed
|
||||
shotsFired.foreach { case (weaponId, fired) =>
|
||||
val landed = math.min(shotsLanded.getOrElse(weaponId, 0), fired)
|
||||
reportFunc(avatarId, weaponId, fired, landed)
|
||||
}
|
||||
shotsFired.clear()
|
||||
shotsLanded.clear()
|
||||
}
|
||||
|
||||
//noinspection ScalaUnusedSymbol
|
||||
private[support] def reportOngoingShotsToAvatar(avatarId: Long, weaponId: Int, fired: Int, landed: Int): Unit = {
|
||||
avatarActor ! AvatarActor.UpdateToolDischarge(EquipmentStat(weaponId, fired, landed, 0, 0))
|
||||
}
|
||||
|
||||
private[support] def reportOngoingShotsToDatabase(avatarId: Long, weaponId: Int, fired: Int, landed: Int): Unit = {
|
||||
ToDatabase.reportToolDischarge(avatarId, EquipmentStat(weaponId, fired, landed, 0, 0))
|
||||
}
|
||||
|
||||
override protected[session] def stop(): Unit = {
|
||||
if (player != null && player.HasGUID) {
|
||||
(prefire ++ shooting).foreach { guid =>
|
||||
|
|
@ -1373,6 +1409,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
fireStateStopMountedMessages(guid)
|
||||
}
|
||||
projectiles.indices.foreach { projectiles.update(_, None) }
|
||||
reportOngoingShots(reportOngoingShotsToDatabase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,30 +267,7 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
|
|||
}
|
||||
|
||||
def SeatPermissionGroup(seatNumber: Int): Option[AccessPermissionGroup.Value] = {
|
||||
if (seatNumber == 0) { //valid in almost all cases
|
||||
Some(AccessPermissionGroup.Driver)
|
||||
} else {
|
||||
Seat(seatNumber) match {
|
||||
case Some(_) =>
|
||||
Definition.controlledWeapons().get(seatNumber) match {
|
||||
case Some(_) =>
|
||||
Some(AccessPermissionGroup.Gunner)
|
||||
case None =>
|
||||
Some(AccessPermissionGroup.Passenger)
|
||||
}
|
||||
case None =>
|
||||
CargoHold(seatNumber) match {
|
||||
case Some(_) =>
|
||||
Some(AccessPermissionGroup.Passenger) //TODO confirm this
|
||||
case None =>
|
||||
if (seatNumber >= trunk.Offset && seatNumber < trunk.Offset + trunk.TotalCapacity) {
|
||||
Some(AccessPermissionGroup.Trunk)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vehicles.SeatPermissionGroup(this.Definition, seatNumber)
|
||||
}
|
||||
|
||||
def Utilities: Map[Int, Utility] = utilities
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package net.psforever.objects
|
||||
|
||||
import net.psforever.objects.ce.TelepadLike
|
||||
import net.psforever.objects.definition.VehicleDefinition
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
|
|
@ -439,4 +440,49 @@ object Vehicles {
|
|||
val turnAway = if (offset.x >= 0) -90f else 90f
|
||||
(obj.Position + offset, (shuttleAngle + turnAway) % 360f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on a mounting index, for a certain mount, to what mounting group does this seat belong?
|
||||
* @param vehicle the vehicle
|
||||
* @param seatNumber specific seat index
|
||||
* @return the seat group designation
|
||||
*/
|
||||
def SeatPermissionGroup(vehicle: Vehicle, seatNumber: Int): Option[AccessPermissionGroup.Value] = {
|
||||
SeatPermissionGroup(vehicle.Definition, seatNumber)
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on a mounting index, for a certain mount, to what mounting group does this seat belong?
|
||||
* @param definition global vehicle specification
|
||||
* @param seatNumber specific seat index
|
||||
* @return the seat group designation
|
||||
*/
|
||||
def SeatPermissionGroup(definition: VehicleDefinition, seatNumber: Int): Option[AccessPermissionGroup.Value] = {
|
||||
if (seatNumber == 0) { //valid in almost all cases
|
||||
Some(AccessPermissionGroup.Driver)
|
||||
} else {
|
||||
definition.Seats
|
||||
.get(seatNumber)
|
||||
.map { _ =>
|
||||
definition.controlledWeapons()
|
||||
.get(seatNumber)
|
||||
.map { _ => AccessPermissionGroup.Gunner }
|
||||
.getOrElse { AccessPermissionGroup.Passenger }
|
||||
}
|
||||
.orElse {
|
||||
definition.Cargo
|
||||
.get(seatNumber)
|
||||
.map { _ => AccessPermissionGroup.Passenger }
|
||||
.orElse {
|
||||
val offset = definition.TrunkOffset
|
||||
val size = definition.TrunkSize
|
||||
if (seatNumber >= offset && seatNumber < offset + size.Width * size.Height) {
|
||||
Some(AccessPermissionGroup.Trunk)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2023 PSForever
|
||||
package net.psforever.objects.avatar.scoring
|
||||
|
||||
final case class EquipmentStat(objectId: Int, shotsFired: Int, shotsLanded: Int, kills: Int)
|
||||
final case class EquipmentStat(objectId: Int, shotsFired: Int, shotsLanded: Int, kills: Int, assists: Int)
|
||||
|
||||
object EquipmentStat {
|
||||
def apply(objectId: Int): EquipmentStat = EquipmentStat(objectId, 0, 1, 0)
|
||||
def apply(objectId: Int): EquipmentStat = EquipmentStat(objectId, 0, 1, 0, 0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class ScoreCard() {
|
|||
curr = ScoreCard.updateEquipmentStat(curr, e)
|
||||
case k: Kill =>
|
||||
curr = curr.copy(kills = k +: curr.kills)
|
||||
curr = ScoreCard.updateEquipmentStat(curr, EquipmentStat(k.info.interaction.cause.attribution, 0, 0, 1))
|
||||
curr = ScoreCard.updateEquipmentStat(curr, EquipmentStat(k.info.interaction.cause.attribution, 0, 0, 1, 0))
|
||||
ScoreCard.updateStatisticsFor(killStatistics, k.info.interaction.cause.attribution, k.victim.Faction)
|
||||
case a: Assist =>
|
||||
curr = curr.copy(assists = a +: curr.assists)
|
||||
|
|
@ -51,14 +51,15 @@ class ScoreCard() {
|
|||
|
||||
object ScoreCard {
|
||||
private def updateEquipmentStat(curr: Life, entry: EquipmentStat): Life = {
|
||||
updateEquipmentStat(curr, entry, entry.objectId, entry.kills)
|
||||
updateEquipmentStat(curr, entry, entry.objectId, entry.kills, entry.assists)
|
||||
}
|
||||
|
||||
private def updateEquipmentStat(
|
||||
curr: Life,
|
||||
entry: EquipmentStat,
|
||||
objectId: Int,
|
||||
killCount: Int
|
||||
killCount: Int,
|
||||
assists: Int
|
||||
): Life = {
|
||||
curr.equipmentStats.indexWhere { a => a.objectId == objectId } match {
|
||||
case -1 =>
|
||||
|
|
|
|||
|
|
@ -5,13 +5,15 @@ import akka.actor.{Actor, Cancellable}
|
|||
import net.psforever.objects.{Vehicle, Vehicles}
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||
import net.psforever.objects.sourcing.VehicleSource
|
||||
import net.psforever.objects.vital.base.DamageResolution
|
||||
import net.psforever.objects.vital.interaction.DamageResult
|
||||
import net.psforever.objects.vital.interaction.{Adversarial, DamageResult}
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.objects.zones.exp.ToDatabase
|
||||
import net.psforever.packet.game.DamageWithPositionMessage
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.DamageWithPositionMessage
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -213,6 +215,18 @@ trait DamageableVehicle
|
|||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, obj.Definition.shieldUiAttribute, 0)
|
||||
)
|
||||
}
|
||||
//database entry
|
||||
cause.adversarial.collect {
|
||||
case Adversarial(attacker, victim: VehicleSource, implement) =>
|
||||
ToDatabase.reportMachineDestruction(
|
||||
attacker.CharId,
|
||||
victim,
|
||||
DamageableObject.HackedBy,
|
||||
DamageableObject.MountedIn.nonEmpty,
|
||||
implement,
|
||||
obj.Zone.Number
|
||||
)
|
||||
}
|
||||
//clean up
|
||||
target.Actor ! Vehicle.Deconstruct(Some(1 minute))
|
||||
DamageableWeaponTurret.DestructionAwareness(obj, cause)
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ class VehicleControl(vehicle: Vehicle)
|
|||
obj.PassengerInSeat(user) match {
|
||||
case Some(seatNumber) =>
|
||||
val vsrc = VehicleSource(vehicle)
|
||||
user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vehicle, vsrc), vehicle.Zone.Number))
|
||||
user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber), vehicle.Zone.Number))
|
||||
//if the driver mount, change ownership if that is permissible for this vehicle
|
||||
if (seatNumber == 0 && !obj.OwnerName.contains(user.Name) && obj.Definition.CanBeOwned.nonEmpty) {
|
||||
//whatever vehicle was previously owned
|
||||
|
|
|
|||
128
src/main/scala/net/psforever/objects/zones/exp/ToDatabase.scala
Normal file
128
src/main/scala/net/psforever/objects/zones/exp/ToDatabase.scala
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) 2022 PSForever
|
||||
package net.psforever.objects.zones.exp
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
import net.psforever.objects.avatar.scoring.EquipmentStat
|
||||
import net.psforever.objects.serverobject.hackable.Hackable.HackInfo
|
||||
import net.psforever.objects.sourcing.VehicleSource
|
||||
import net.psforever.persistence
|
||||
import net.psforever.types.Vector3
|
||||
import net.psforever.util.Database.ctx
|
||||
import net.psforever.util.Database.ctx._
|
||||
|
||||
import scala.util.Success
|
||||
|
||||
object ToDatabase {
|
||||
def reportKillBy(
|
||||
killerId: Long,
|
||||
victimId: Long,
|
||||
victimExoSuitId: Int,
|
||||
victimMounted: Int,
|
||||
weaponId: Int,
|
||||
zoneId: Int,
|
||||
position: Vector3,
|
||||
exp: Long
|
||||
): Unit = {
|
||||
ctx.run(query[persistence.Killactivity]
|
||||
.insert(
|
||||
_.victimId -> lift(victimId),
|
||||
_.killerId -> lift(killerId),
|
||||
_.victimExosuit -> lift(victimExoSuitId),
|
||||
_.victimMounted -> lift(victimMounted),
|
||||
_.weaponId -> lift(weaponId),
|
||||
_.zoneId -> lift(zoneId),
|
||||
_.px -> lift((position.x * 1000).toInt),
|
||||
_.py -> lift((position.y * 1000).toInt),
|
||||
_.pz -> lift((position.z * 1000).toInt),
|
||||
_.exp -> lift(exp)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def reportToolDischarge(avatarId: Long, stats: EquipmentStat): Unit = {
|
||||
val result = for {
|
||||
res <- ctx.run(
|
||||
query[persistence.Weaponstatsession]
|
||||
.filter(_.avatarId == lift(avatarId))
|
||||
.filter(_.weaponId == lift(stats.objectId))
|
||||
.update(
|
||||
_.shotsFired -> lift(stats.shotsFired),
|
||||
_.shotsLanded -> lift(stats.shotsLanded)
|
||||
)
|
||||
)
|
||||
} yield res
|
||||
result.onComplete {
|
||||
case Success(rowCount) if rowCount.longValue > 0 => ()
|
||||
case _ =>
|
||||
ctx.run(query[persistence.Weaponstatsession]
|
||||
.insert(
|
||||
_.avatarId -> lift(avatarId),
|
||||
_.weaponId -> lift(stats.objectId),
|
||||
_.shotsFired -> lift(stats.shotsFired),
|
||||
_.shotsLanded -> lift(stats.shotsLanded),
|
||||
_.kills -> lift(0),
|
||||
_.assists -> lift(0),
|
||||
_.sessionId -> lift(-1L)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def reportAssistKills(avatarId: Long, weaponId: Int, assists: Int): Unit = {
|
||||
val result = for {
|
||||
res <- ctx.run(
|
||||
query[persistence.Weaponstatsession]
|
||||
.filter(_.avatarId == lift(avatarId))
|
||||
.filter(_.weaponId == lift(weaponId))
|
||||
.update(
|
||||
_.assists -> lift(assists)
|
||||
)
|
||||
)
|
||||
} yield res
|
||||
result.onComplete {
|
||||
case Success(rowCount) if rowCount.longValue > 0 => ()
|
||||
case _ =>
|
||||
ctx.run(query[persistence.Weaponstatsession]
|
||||
.insert(
|
||||
_.avatarId -> lift(avatarId),
|
||||
_.weaponId -> lift(weaponId),
|
||||
_.assists -> lift(assists),
|
||||
_.shotsFired -> lift(0),
|
||||
_.shotsLanded -> lift(0),
|
||||
_.kills -> lift(0),
|
||||
_.sessionId -> lift(-1L)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def reportMachineDestruction(
|
||||
avatarId: Long,
|
||||
machine: VehicleSource,
|
||||
hackState: Option[HackInfo],
|
||||
isCargo: Boolean,
|
||||
weaponId: Int,
|
||||
zoneNumber: Int
|
||||
): Unit = {
|
||||
import net.psforever.util.Database.ctx
|
||||
import net.psforever.util.Database.ctx._
|
||||
val normalFaction = machine.Faction.id
|
||||
val hackedFaction = hackState.map { _.player.Faction }.getOrElse(normalFaction)
|
||||
val machinePosition = machine.Position
|
||||
ctx.run(query[persistence.Machinedestroyedinstance]
|
||||
.insert(
|
||||
_.avatarId -> lift(avatarId),
|
||||
_.weaponId -> lift(weaponId),
|
||||
_.machineType -> lift(machine.Definition.ObjectId),
|
||||
_.machineFaction -> lift(normalFaction),
|
||||
_.hackedFaction -> lift(hackedFaction),
|
||||
_.asCargo -> lift(isCargo),
|
||||
_.zoneNum -> lift(zoneNumber),
|
||||
_.px -> lift((machinePosition.x * 1000).toInt),
|
||||
_.py -> lift((machinePosition.y * 1000).toInt),
|
||||
_.pz -> lift((machinePosition.z * 1000).toInt)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ case class Account(
|
|||
inactive: Boolean = false,
|
||||
gm: Boolean = false,
|
||||
lastFactionId: Int = 3,
|
||||
avatarLoggedIn: Long,
|
||||
sessionId: Long,
|
||||
token: Option[String],
|
||||
tokenCreated: Option[LocalDateTime]
|
||||
)
|
||||
|
|
|
|||
16
src/main/scala/net/psforever/persistence/Killactivity.scala
Normal file
16
src/main/scala/net/psforever/persistence/Killactivity.scala
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2022 PSForever
|
||||
package net.psforever.persistence
|
||||
|
||||
case class Killactivity(
|
||||
index: Int,
|
||||
killerId: Long,
|
||||
victimId: Long,
|
||||
victimExosuit: Int,
|
||||
victimMounted: Int, //object type id * 10 + seat type
|
||||
weaponId: Int,
|
||||
zoneId: Int,
|
||||
px: Int, //Position.x * 1000
|
||||
py: Int, //Position.y * 1000
|
||||
pz: Int, //Position.z * 1000
|
||||
exp: Long
|
||||
)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2022 PSForever
|
||||
package net.psforever.persistence
|
||||
|
||||
import org.joda.time.LocalDateTime
|
||||
|
||||
case class Machinedestroyedinstance(
|
||||
index: Int,
|
||||
avatarId: Long,
|
||||
weaponId: Int,
|
||||
machineType: Int,
|
||||
machineFaction: Int,
|
||||
hackedFaction: Int,
|
||||
asCargo: Boolean,
|
||||
zoneNum: Int,
|
||||
px: Int, //Position.x * 1000
|
||||
py: Int, //Position.y * 1000
|
||||
pz: Int, //Position.z * 1000
|
||||
timestamp: LocalDateTime = LocalDateTime.now()
|
||||
)
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) 2022 PSForever
|
||||
package net.psforever.persistence
|
||||
|
||||
case class Weaponstatsession(
|
||||
avatarId: Long,
|
||||
weaponId: Int,
|
||||
shotsFired: Int,
|
||||
shotsLanded: Int,
|
||||
kills: Int,
|
||||
assists: Int,
|
||||
sessionId: Long
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue