Skip to content

Commit

Permalink
Bug #140: Add a column to the alliances table to store whether the al…
Browse files Browse the repository at this point in the history
…liance is active or not. Updated it in a daily cron.
  • Loading branch information
codeka committed Nov 13, 2018
1 parent 2f85146 commit c9657a0
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 119 deletions.
9 changes: 9 additions & 0 deletions server/data/schema/schema-006.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

-- Add a flag to indicate whether the alliance is active or not. Inactive alliances are filtered
-- out of search results by default. An inactive alliance is one where nobody has logged in for a
-- while.
ALTER TABLE alliances
ADD COLUMN is_active INTEGER;

-- Everybody's active by default (until a cron job runs and resets it).
UPDATE alliances SET is_active = 1;
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class CronJobRegistry {
sCronJobs.put("find-alts", FindAltAccountsCronJob.class);
sCronJobs.put("update-dashboard", UpdateDashboardCronJob.class);
sCronJobs.put("fix-names", FixNamesCronJob.class);
sCronJobs.put("update-active-alliances", UpdateActiveAlliancesCronJob.class);
}

public static CronJob getJob(String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,138 +16,138 @@
/**
* An abandoned empire is one where the user hasn't logged in for a while, and they only have one
* star under their control.
*
* <p>
* Abandoned empires have two things happen to them:
* 1. their star becomes available for new signups and
* 2. their empire name becomes available for new signups.
*
* 1. their star becomes available for new signups and
* 2. their empire name becomes available for new signups.
* <p>
* If they later log in and their star has been taken, they'll get a standard "empire reset"
* message with help text explaining that their empire expired. If they log in again and their
* name has been changed, they'll be required to choose another.
*/
public class FindAbandonedEmpiresCronJob extends CronJob {
private static final Log log = new Log("FindAbandonedEmpiresCronJob");

@Override
public void run(String extra) throws Exception {
ArrayList<Integer> abandonedEmpires = new ArrayList<Integer>();

// first, find all empires not already marked "abandoned" that only have one star and
// haven't logged in for two weeks
String sql = "SELECT id, name" +
" FROM empires" +
" INNER JOIN (" +
"SELECT empire_id, MAX(date) AS last_login FROM empire_logins" +
" GROUP BY empire_id) logins ON logins.empire_id = empires.id" +
" INNER JOIN (" +
"SELECT empire_id, COUNT(*) AS num_stars FROM empire_presences" +
" GROUP BY empire_id) stars ON stars.empire_id = empires.id" +
" WHERE state = " + Empire.State.ACTIVE.getValue() +
" AND last_login < DATE_ADD(NOW(), INTERVAL -14 DAY)" +
" AND num_stars <= 1";
try (SqlStmt stmt = DB.prepare(sql)) {
SqlResult res = stmt.select();
while (res.next()) {
int empireID = res.getInt(1);
String empireName = res.getString(2);
log.info(String.format(Locale.ENGLISH,
"Empire #%d (%s) has not logged in for two weeks, marking abandoned.",
empireID, empireName));
abandonedEmpires.add(empireID);
}
}

sql = "UPDATE empires SET state = ? WHERE id = ?";
try (SqlStmt stmt = DB.prepare(sql)) {
stmt.setInt(1, Empire.State.ABANDONED.getValue());
for (Integer empireID : abandonedEmpires) {
stmt.setInt(2, empireID);
stmt.update();
}
}
private static final Log log = new Log("FindAbandonedEmpiresCronJob");

@Override
public void run(String extra) throws Exception {
ArrayList<Integer> abandonedEmpires = new ArrayList<Integer>();

// first, find all empires not already marked "abandoned" that only have one star and
// haven't logged in for two weeks
String sql = "SELECT id, name" +
" FROM empires" +
" INNER JOIN (" +
"SELECT empire_id, MAX(date) AS last_login FROM empire_logins" +
" GROUP BY empire_id) logins ON logins.empire_id = empires.id" +
" INNER JOIN (" +
"SELECT empire_id, COUNT(*) AS num_stars FROM empire_presences" +
" GROUP BY empire_id) stars ON stars.empire_id = empires.id" +
" WHERE state = " + Empire.State.ACTIVE.getValue() +
" AND last_login < DATE_ADD(NOW(), INTERVAL -14 DAY)" +
" AND num_stars <= 1";
try (SqlStmt stmt = DB.prepare(sql)) {
SqlResult res = stmt.select();
while (res.next()) {
int empireID = res.getInt(1);
String empireName = res.getString(2);
log.info(String.format(Locale.ENGLISH,
"Empire #%d (%s) has not logged in for two weeks, marking abandoned.",
empireID, empireName));
abandonedEmpires.add(empireID);
}
}

updateAbandonedStars();
sql = "UPDATE empires SET state = ? WHERE id = ?";
try (SqlStmt stmt = DB.prepare(sql)) {
stmt.setInt(1, Empire.State.ABANDONED.getValue());
for (Integer empireID : abandonedEmpires) {
stmt.setInt(2, empireID);
stmt.update();
}
}

private void updateAbandonedStars() throws Exception {
String sql = "SELECT stars.id, empires.id" +
" FROM stars" +
" INNER JOIN empire_presences ON empire_presences.star_id = stars.id" +
" INNER JOIN empires ON empires.id = empire_presences.empire_id" +
" WHERE empires.state = " + Empire.State.ABANDONED.getValue();
try (SqlStmt stmt = DB.prepare(sql)) {
SqlResult res = stmt.select();
while (res.next()) {
int starID = res.getInt(1);
int empireID = res.getInt(2);
try {
updateAbandonedStar(starID, empireID);
} catch (Exception e) {
log.error("Error marking star abandoned, starID="+starID, e);
}
}
updateAbandonedStars();
}

private void updateAbandonedStars() throws Exception {
String sql = "SELECT stars.id, empires.id" +
" FROM stars" +
" INNER JOIN empire_presences ON empire_presences.star_id = stars.id" +
" INNER JOIN empires ON empires.id = empire_presences.empire_id" +
" WHERE empires.state = " + Empire.State.ABANDONED.getValue();
try (SqlStmt stmt = DB.prepare(sql)) {
SqlResult res = stmt.select();
while (res.next()) {
int starID = res.getInt(1);
int empireID = res.getInt(2);
try {
updateAbandonedStar(starID, empireID);
} catch (Exception e) {
log.error("Error marking star abandoned, starID=" + starID, e);
}
}
}

private void updateAbandonedStar(int starID, int empireID) throws Exception {
Star star = new StarController().getStar(starID);
float distanceToNonAbandonedEmpire = 0.0f;

// find all stars around us with non-abandoned empires on them
String sql = "SELECT sectors.x, sectors.y, stars.x, stars.y" +
" FROM stars" +
" INNER JOIN sectors ON sectors.id = stars.sector_id" +
" INNER JOIN empire_presences ON empire_presences.star_id = stars.id" +
" INNER JOIN empires ON empire_presences.empire_id = empires.id" +
" WHERE sectors.x < ? AND sectors.x > ?" +
" AND sectors.y < ? AND sectors.y > ?" +
" AND empires.state = " + Empire.State.ACTIVE.getValue();
try (SqlStmt stmt = DB.prepare(sql)) {
stmt.setLong(1, star.getSectorX() + 3);
stmt.setLong(2, star.getSectorX() - 3);
stmt.setLong(3, star.getSectorY() + 3);
stmt.setLong(4, star.getSectorY() - 3);
SqlResult res = stmt.select();

while (res.next()) {
long sectorX = res.getLong(1);
long sectorY = res.getLong(2);
int offsetX = res.getInt(3);
int offsetY = res.getInt(4);

float distance = Sector.distanceInParsecs(star, sectorX, sectorY, offsetX, offsetY);
if (distanceToNonAbandonedEmpire < 0.001f
|| distance < distanceToNonAbandonedEmpire) {
distanceToNonAbandonedEmpire = distance;
}
}
}

private void updateAbandonedStar(int starID, int empireID) throws Exception {
Star star = new StarController().getStar(starID);
float distanceToNonAbandonedEmpire = 0.0f;

// find all stars around us with non-abandoned empires on them
String sql = "SELECT sectors.x, sectors.y, stars.x, stars.y" +
" FROM stars" +
" INNER JOIN sectors ON sectors.id = stars.sector_id" +
" INNER JOIN empire_presences ON empire_presences.star_id = stars.id" +
" INNER JOIN empires ON empire_presences.empire_id = empires.id" +
" WHERE sectors.x < ? AND sectors.x > ?" +
" AND sectors.y < ? AND sectors.y > ?" +
" AND empires.state = " + Empire.State.ACTIVE.getValue();
try (SqlStmt stmt = DB.prepare(sql)) {
stmt.setLong(1, star.getSectorX() + 3);
stmt.setLong(2, star.getSectorX() - 3);
stmt.setLong(3, star.getSectorY() + 3);
stmt.setLong(4, star.getSectorY() - 3);
SqlResult res = stmt.select();

while (res.next()) {
long sectorX = res.getLong(1);
long sectorY = res.getLong(2);
int offsetX = res.getInt(3);
int offsetY = res.getInt(4);

float distance = Sector.distanceInParsecs(star, sectorX, sectorY, offsetX, offsetY);
if (distanceToNonAbandonedEmpire < 0.001f
|| distance < distanceToNonAbandonedEmpire) {
distanceToNonAbandonedEmpire = distance;
}
}
}

if (distanceToNonAbandonedEmpire < 0.0001f) {
distanceToNonAbandonedEmpire = 9999.0f;
}
if (distanceToNonAbandonedEmpire < 0.0001f) {
distanceToNonAbandonedEmpire = 9999.0f;
}

double distanceToCentre = Math.sqrt((star.getSectorX() * star.getSectorX())
+ (star.getSectorY() * star.getSectorY()));

try (Transaction t = DB.beginTransaction()) {
sql = "DELETE FROM abandoned_stars WHERE star_id = ?";
try (SqlStmt stmt = t.prepare(sql)) {
stmt.setInt(1, star.getID());
stmt.update();
}

sql = "INSERT INTO abandoned_stars (star_id, empire_id, distance_to_centre," +
" distance_to_non_abandoned_empire) VALUES (?, ?, ?, ?)";
try (SqlStmt stmt = t.prepare(sql)) {
stmt.setInt(1, star.getID());
stmt.setInt(2, empireID);
stmt.setDouble(3, distanceToCentre);
stmt.setDouble(4, distanceToNonAbandonedEmpire);
stmt.update();
}

t.commit();
}
double distanceToCentre = Math.sqrt((star.getSectorX() * star.getSectorX())
+ (star.getSectorY() * star.getSectorY()));

try (Transaction t = DB.beginTransaction()) {
sql = "DELETE FROM abandoned_stars WHERE star_id = ?";
try (SqlStmt stmt = t.prepare(sql)) {
stmt.setInt(1, star.getID());
stmt.update();
}

sql = "INSERT INTO abandoned_stars (star_id, empire_id, distance_to_centre," +
" distance_to_non_abandoned_empire) VALUES (?, ?, ?, ?)";
try (SqlStmt stmt = t.prepare(sql)) {
stmt.setInt(1, star.getID());
stmt.setInt(2, empireID);
stmt.setDouble(3, distanceToCentre);
stmt.setDouble(4, distanceToNonAbandonedEmpire);
stmt.update();
}

t.commit();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package au.com.codeka.warworlds.server.cron;

import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Duration;

import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

import au.com.codeka.warworlds.server.data.DB;
import au.com.codeka.warworlds.server.data.SqlResult;
import au.com.codeka.warworlds.server.data.SqlStmt;

/**
* Goes through all the alliances and marks inactive any alliance whose member has not logged in for
* a while.
*/
public class UpdateActiveAlliancesCronJob extends CronJob {
/**
* If no empire has logged in within this number of days, we'll consider the alliance inactive.
*/
long ACTIVE_DAYS_SINCE_LOGIN = 60;

@Override
public void run(String extra) throws Exception {
Map<Integer, DateTime> allianceLastLogins = new TreeMap<>();
String sql = "SELECT" +
" alliances.id," +
" MAX(empires.last_login) as last_login" +
" FROM alliances alliances" +
" INNER JOIN (" +
" SELECT" +
" empires.id," +
" empires.alliance_id," +
" logins.last_login" +
" FROM empires empires" +
" INNER JOIN (" +
" SELECT" +
" empire_id," +
" MAX(date) AS last_login" +
" FROM beta.empire_logins" +
" GROUP BY empire_id" +
" ) logins ON logins.empire_id = empires.id" +
" ) empires" +
" ON alliances.id = empires.alliance_id" +
" GROUP BY alliances.id";
try (SqlStmt stmt = DB.prepare(sql)) {
SqlResult res = stmt.select();
while (res.next()) {
int allianceID = res.getInt(1);
DateTime lastLogin = res.getDateTime(2);
allianceLastLogins.put(allianceID, lastLogin);
}
}

try (SqlStmt stmt = DB.prepare("UPDATE alliances SET is_active = ? WHERE id = ?")) {
for (Map.Entry<Integer, DateTime> entry : allianceLastLogins.entrySet()) {
DateTime lastLogin = entry.getValue();
Days daysSinceLogin = Days.daysBetween(lastLogin, DateTime.now());
if (daysSinceLogin.getDays() > ACTIVE_DAYS_SINCE_LOGIN) {
stmt.setInt(1, 0);
} else {
stmt.setInt(1, 1);
}
stmt.setInt(2, entry.getKey());
stmt.update();
}
}
}
}

0 comments on commit c9657a0

Please sign in to comment.