X-Git-Url: https://ruin.nu/git/?a=blobdiff_plain;f=lib%2FNDWeb%2FController%2FMembers.pm;h=e8a7d0e70e3d509b23a62787c5a9c85c502e5e20;hb=5aebbd7109de51640f6049631093f004d66583d9;hp=8c44e056590cb0e5832c4ab3281c1f79a79b3a71;hpb=5b6c3c7d0003d636c1c3da6d25734e4aad5736a5;p=ndwebbie.git diff --git a/lib/NDWeb/Controller/Members.pm b/lib/NDWeb/Controller/Members.pm index 8c44e05..e8a7d0e 100644 --- a/lib/NDWeb/Controller/Members.pm +++ b/lib/NDWeb/Controller/Members.pm @@ -2,6 +2,8 @@ package NDWeb::Controller::Members; use strict; use warnings; +use feature ":5.10"; +use Try::Tiny; use parent 'Catalyst::Controller'; use NDWeb::Include; @@ -29,21 +31,21 @@ sub index : Path : Args(0) { $c->stash(error => $c->flash->{error}); - $c->stash(comma => \&comma_value); - $c->stash(u => $dbh->selectrow_hashref(q{SELECT planet,defense_points + $c->stash(u => $dbh->selectrow_hashref(q{SELECT pid AS planet,defense_points ,attack_points,scan_points,humor_points - , (attack_points+defense_points+scan_points/20) as total_points - , sms,rank,hostmask FROM users WHERE uid = ? + , (attack_points+defense_points+scan_points/20)::NUMERIC(5,1) as total_points + , sms,rank,hostmask,call_if_needed,sms_note,defprio + FROM users_defprio WHERE uid = ? },undef,$c->user->id) ); - $c->stash(groups => $dbh->selectrow_array(q{SELECT array_accum(groupname) + $c->stash(groups => $dbh->selectrow_array(q{SELECT array_agg(groupname) FROM groups g NATURAL JOIN groupmembers gm WHERE uid = $1 },undef,$c->user->id) ); - $c->stash(p => $dbh->selectrow_hashref(q{SELECT id,x,y,z, ruler, planet,race, + $c->stash(p => $dbh->selectrow_hashref(q{SELECT pid AS id,x,y,z, ruler, planet,race, size, size_gain, size_gain_day, score,score_gain,score_gain_day, value,value_gain,value_gain_day, @@ -53,61 +55,19 @@ sub index : Path : Args(0) { valuerank,valuerank_gain,valuerank_gain_day, xprank,xprank_gain,xprank_gain_day from current_planet_stats_full p - WHERE id = ? + WHERE pid = ? },undef,$c->user->planet) ); my $calls = $dbh->prepare(q{ - SELECT id,landing_tick,dc,curreta - ,array_accum(race::text) AS race - ,array_accum(amount) AS amount - ,array_accum(eta) AS eta - ,array_accum(shiptype) AS shiptype - ,array_accum(coords) AS attackers - FROM (SELECT c.id, c.landing_tick - ,dc.username AS dc, (c.landing_tick - tick()) AS curreta - ,p2.race, i.amount, i.eta, i.shiptype, p2.alliance - ,coords(p2.x,p2.y,p2.z) - FROM calls c - LEFT OUTER JOIN incomings i ON i.call = c.id - LEFT OUTER JOIN current_planet_stats p2 ON i.sender = p2.id - LEFT OUTER JOIN users dc ON c.dc = dc.uid - WHERE c.member = $1 AND c.landing_tick >= tick() - GROUP BY c.id, c.landing_tick, dc.username - ,p2.race,i.amount,i.eta,i.shiptype,p2.alliance,p2.x,p2.y,p2.z - ) c - GROUP BY id, landing_tick,dc,curreta +SELECT * FROM defcalls +WHERE uid = $1 AND landing_tick >= tick() +ORDER BY landing_tick DESC }); - $calls->execute($c->user->id); $c->stash(calls => $calls->fetchall_arrayref({}) ); - my $query = $dbh->prepare(q{SELECT f.id, coords(x,y,z), target, mission - , f.amount, tick, back -FROM fleets f -LEFT OUTER JOIN current_planet_stats p ON f.target = p.id -WHERE NOT ingal AND f.uid = ? AND f.sender = ? AND - (back >= ? OR (tick >= tick() - 24 AND name = 'Main')) -GROUP BY f.id, x,y,z, mission, tick,back,f.amount,f.target -ORDER BY x,y,z,mission,tick - }); - - my $ships = $dbh->prepare(q{SELECT ship,amount FROM fleet_ships - WHERe id = ? ORDER BY num - }); - - $query->execute($c->user->id,$c->user->planet,$c->stash->{TICK}); - my @fleets; - while (my $fleet = $query->fetchrow_hashref){ - my @ships; - $ships->execute($fleet->{id}); - while (my $ship = $ships->fetchrow_hashref){ - push @ships,$ship; - } - $fleet->{ships} = \@ships; - push @fleets,$fleet; - } - $c->stash(fleets => \@fleets); + $c->stash(fleets => member_fleets($dbh, $c->user->id,$c->user->planet)); my $announcements = $dbh->prepare(q{SELECT ft.ftid, u.username,ft.subject, count(NULLIF(COALESCE(fp.time > ftv.time,TRUE),FALSE)) AS unread,count(fp.fpid) AS posts, @@ -123,6 +83,12 @@ ORDER BY x,y,z,mission,tick }); $announcements->execute($c->user->id); $c->stash(announcements => $announcements->fetchall_arrayref({}) ); + + my ($attackgroups) = $dbh->selectrow_array(q{ +SELECT array_agg(gid) FROM groupmembers WHERE gid IN ('x','y','z') AND uid = $1 + }, undef, $c->user->id); + $c->stash(attackgroups => $attackgroups); + } sub posthostupdate : Local { @@ -135,54 +101,66 @@ sub posthostupdate : Local { $c->res->redirect($c->uri_for('')); } +sub postattackgroups : Local { + my ( $self, $c ) = @_; + my $dbh = $c->model; + + my @groups = $c->req->param('class'); + $dbh->do(q{DELETE FROM groupmembers WHERE gid IN ('x','y','z') AND gid <> ALL($1) AND uid = $2 + },undef, \@groups, $c->user->id); + + $dbh->do(q{INSERT INTO groupmembers (uid,gid) ( + SELECT $2, gid FROM unnest($1::text[]) AS gid WHERE gid IN ('x','y','z') + EXCEPT + SELECT uid,gid FROM groupmembers WHERE uid = $2 + )},undef, \@groups, $c->user->id); + + $c->res->redirect($c->uri_for('')); +} + sub postsmsupdate : Local { my ( $self, $c ) = @_; my $dbh = $c->model; - $dbh->do(q{UPDATE users SET sms = ? WHERE uid = ? - },undef, html_escape $c->req->param('sms'), $c->user->id); + my $callme = $c->req->param('callme') || 0; + $dbh->do(q{ +UPDATE users SET sms = $1, call_if_needed = $2, sms_note = $3 WHERE uid = $4 + },undef, html_escape $c->req->param('sms'),$callme + ,$c->req->param('smsnote'), $c->user->id); $c->res->redirect($c->uri_for('')); } -sub postfleetupdate : Local { +sub postowncoords : Local { my ( $self, $c ) = @_; my $dbh = $c->model; - my $fleet = $c->req->param('fleet'); - $fleet =~ s/,//g; - my $amount = 0; - my @ships; - while ($fleet =~ m/((?:[A-Z][a-z]+ )*[A-Z][a-z]+)\s+(\d+)/g){ - $amount += $2; - push @ships, [$1,$2]; - } - if ($amount){ - $dbh->begin_work; - eval{ - my $insert = $dbh->prepare(q{INSERT INTO fleets - (uid,sender,name,mission,tick,amount) - VALUES (?,?,'Main','Full fleet',tick(),?) RETURNING id}); - my ($id) = $dbh->selectrow_array($insert,undef,$c->user->id - ,$c->user->planet,$amount); - $insert = $dbh->prepare(q{INSERT INTO fleet_ships - (id,ship,amount) VALUES (?,?,?)}); - for my $s (@ships){ - unshift @{$s},$id; - $insert->execute(@{$s}); - } - $dbh->commit; - }; - if ($@){ - if ($@ =~ m/insert or update on table "fleet_ships" violates foreign key constraint "fleet_ships_ship_fkey"\s+DETAIL:\s+Key \(ship\)=\(([^)]+)\)/){ - $c->flash( error => "'$1' is NOT a valid ship"); - }else{ - $c->flash( error => $@); + if ($c->user->planet){ + $c->flash(error => 'You already have a planet set.' + .' Contact a HC if they need to be changed'); + }elsif (my ($x,$y,$z) = $c->req->param('planet') =~ m/(\d+)\D+(\d+)\D+(\d+)/){ + my $planet = $dbh->selectrow_array(q{SELECT planetid($1,$2,$3,TICK()) + },undef,$x,$y,$z); + + if ($planet){ + eval { + $dbh->do(q{UPDATE users SET pid = ? WHERE uid = ? + },undef, $planet , $c->user->id); + }; + given ($@){ + when (''){} + when (/duplicate key value violates/){ + $c->flash(error => "The coords $x:$y:$z are already in use. Talk to hc if these are really your coords.") + } + default { + $c->flash(error => $@) + } } - $dbh->rollback; + }else{ + $c->flash(error => "No planet at coords: $x:$y:$z"); } }else{ - $c->flash( error => 'Fleet does not contain any ships'); + $c->flash(error => $c->req->param('planet') . " are not valid coords."); } $c->res->redirect($c->uri_for('')); @@ -197,9 +175,9 @@ sub postfleetsupdates : Local { }); $dbh->begin_work; if ($c->req->param('cmd') eq 'Recall Fleets'){ - my $updatefleets = $dbh->prepare(q{UPDATE fleets - SET back = tick() + (tick() - (tick - eta)) - WHERE uid = ? AND id = ? AND back > tick()+eta + my $updatefleets = $dbh->prepare(q{UPDATE launch_confirmations + SET back = tick() + (tick() - (landing_tick - eta)) + WHERE uid = ? AND fid = ? AND back >= tick()+eta }); for my $param ($c->req->param()){ @@ -209,8 +187,8 @@ sub postfleetsupdates : Local { } } }elsif ($c->req->param('cmd') eq 'Change Fleets'){ - my $updatefleets = $dbh->prepare(q{UPDATE fleets - SET back = ? WHERE uid = ? AND id = ?}); + my $updatefleets = $dbh->prepare(q{UPDATE launch_confirmations + SET back = ? WHERE uid = ? AND fid = ?}); for my $param ($c->req->param()){ if ($param =~ /^change:(\d+)$/){ @@ -230,27 +208,35 @@ sub ircrequest : Local { my $dbh = $c->model; $c->stash(reply => $c->flash->{reply}); - $c->stash(channels => ['def','scan','members']); + $c->stash(channels => ['scan','members','def']); } sub postircrequest : Local { my ( $self, $c ) = @_; my $dbh = $c->model; - my $query = $dbh->prepare(q{INSERT INTO irc_requests - (uid,channel,message) VALUES($1,$2,$3) + if ($c->req->param('channel')){ + my $query = $dbh->prepare(q{ +INSERT INTO irc_requests (uid,channel,message) VALUES($1,$2,$3) }); - $query->execute($c->user->id,$c->req->param('channel'),$c->req->param('message')); + $query->execute($c->user->id,$c->req->param('channel'),$c->req->param('message')); + $c->signal_bots; - $c->flash(reply => "Msg sent to: ".$c->req->param('channel')); - $c->res->redirect($c->uri_for('ircrequest')); + $c->flash(reply => "Msg sent to: ".$c->req->param('channel')); + $c->res->redirect($c->uri_for('ircrequest')); + }else{ + $c->stash(ircmessage => $c->req->param('message')); + $c->go('ircrequest'); + } } sub points : Local { my ( $self, $c, $order ) = @_; my $dbh = $c->model; - if ($order && $order =~ /^((?:defense|attack|total|humor|scan|raid)_points)$/){ + $order //= 'total_points'; + if ($order ~~ /^((?:defense|attack|total|humor|scan|raid)_points)$/ + || $order ~~ /^(defprio)$/){ $order = "$1 DESC"; }else{ $order = 'total_points DESC'; @@ -259,20 +245,57 @@ sub points : Local { my $limit = 'LIMIT 10'; $limit = '' if $c->check_user_roles(qw/members_points_nolimit/); - my $query = $dbh->prepare(qq{SELECT username,defense_points,attack_points - ,scan_points,humor_points - ,(attack_points+defense_points+scan_points/20) as total_points - , count(NULLIF(rc.launched,FALSE)) AS raid_points - FROM users u LEFT OUTER JOIN raid_claims rc USING (uid) - WHERE uid IN (SELECT uid FROM groupmembers WHERE gid = 2) - GROUP BY username,defense_points,attack_points,scan_points,humor_points,rank - ORDER BY $order $limit}); + my $query = $dbh->prepare(q{ +SELECT username,defense_points,attack_points + ,scan_points,humor_points,defprio + ,(attack_points+defense_points+scan_points/20)::NUMERIC(4,0) as total_points + , count(NULLIF(rc.launched,FALSE)) AS raid_points +FROM users_defprio u LEFT OUTER JOIN raid_claims rc USING (uid) +WHERE uid IN (SELECT uid FROM groupmembers WHERE gid = 'M') +GROUP BY username,defense_points,attack_points,scan_points,humor_points,defprio +ORDER BY } . "$order $limit" + ); $query->execute; - my @members; - while (my $member = $query->fetchrow_hashref){ - push @members,$member; + $c->stash(members => $query->fetchall_arrayref({})); +} + +sub stats : Local { + my ( $self, $c, $order ) = @_; + my $dbh = $c->model; + + $order //= 'score'; + if ($order ~~ /^(scre|value|xp|size|race)$/){ + $order = "$1rank"; + }else{ + $order = 'scorerank'; } - $c->stash(members => \@members); + $order .= ',race' if $order eq 'racerank'; + + my $limit = 'LIMIT 10'; + $limit = '' if $c->check_user_roles(qw/members_points_nolimit/); + + my ($races) = $dbh->selectrow_array(q{SELECT enum_range(null::race)::text[]}); + $c->stash(races => $races); + my $query = $dbh->prepare(q{ +SELECT nick + ,rank() OVER(ORDER BY score DESC) AS scorerank + ,rank() OVER(ORDER BY value DESC) AS valuerank + ,rank() OVER(ORDER BY xp DESC) AS xprank + ,rank() OVER(ORDER BY size DESC) AS sizerank + ,rank() OVER(PARTITION BY race ORDER BY score DESC) AS racerank + ,race +FROM current_planet_stats +WHERE alliance = 'NewDawn' + AND race = ANY($1) +ORDER BY } . "$order $limit"); + my @race = $c->req->param('race'); + my %race = map { $_ => 1 } @race; + $c->stash(race => \%race); + unless (@race){ + @race = @$races; + } + $query->execute(\@race); + $c->stash(members => $query->fetchall_arrayref({})); } sub addintel : Local { @@ -300,6 +323,11 @@ sub postintelmessage : Local { } } + my ($coords,$tick) = $c->model->selectrow_array(q{ +SELECT coords(x,y,z), tick() FROM current_planet_stats WHERE pid = $1 + }, undef, $c->user->planet); + + $c->req->param(message => "[i]Posted by $coords at tick $tick [/i]\n\n" . $c->req->param('message')); $c->forward('/forum/insertThread',[12]); $c->forward('/forum/insertPost',[$c->stash->{thread}]); $c->flash(intelmessage => 1); @@ -315,17 +343,17 @@ sub insertintel : Private { $dbh->begin_work; my $findscan = $dbh->prepare(q{SELECT scan_id FROM scans - WHERE scan_id = ? AND tick >= tick() - 168 AND groupscan = ? + WHERE scan_id = LOWER(?) AND tick >= tick() - 168 AND groupscan = ? }); my $addscan = $dbh->prepare(q{INSERT INTO scans (scan_id,tick,uid,groupscan) - VALUES (?,tick(),?,?) + VALUES (LOWER(?),tick(),?,?) }); my $addpoint = $dbh->prepare(q{UPDATE users SET scan_points = scan_points + 1 WHERE uid = ? }); my @scans; my $intel = $c->req->param('message'); - while ($intel =~ m{http://[\w.]+/.+?scan(_id|_grp)?=(\d+)}g){ + while ($intel =~ m{http://[\w.]+/.+?scan(_id|_grp)?=(\w+)}g){ my $groupscan = (defined $1 && $1 eq '_grp') || 0; my %scan; $scan{id} = $2; @@ -345,20 +373,20 @@ sub insertintel : Private { unless ($tick =~ /^(\d+)$/){ $tick = $c->stash->{game}->{tick}; } - my $addintel = $dbh->prepare(q{INSERT INTO fleets + my $addintel = $dbh->prepare(q{INSERT INTO intel (name,mission,tick,target,sender,eta,amount,ingal,back,uid) VALUES($1,$2,$3,planetid($4,$5,$6,$10),planetid($7,$8,$9,$10) ,$11,$12,$13,$14,$15) }); my @intel; while ($intel =~ m/(\d+):(\d+):(\d+)\*?\s+(\d+):(\d+):(\d+) - \*?\s+(.+)(?:Ter|Cat|Xan|Zik|Etd)? - \s+(\d+)\s+(Attack|Defend)\s+(\d+)/gx){ + \*?\s+(A|D)\s+(.+?)\s+(?:(?:Ter|Cat|Xan|Zik|Etd)\s+)?(\d+)\s+(\d+)/gx){ my $ingal = ($1 == $4 && $2 == $5) || 0; my $lt = $tick + $10; my $back = ($ingal ? $lt + 4 : undef); + my $mission = $7 eq 'A' ? 'Attack' : 'Defend'; eval { - $addintel->execute($7,$9,$lt,$1,$2,$3,$4,$5,$6,$tick,$10,$8 + $addintel->execute($8,$mission,$lt,$1,$2,$3,$4,$5,$6,$tick,$10,$9 ,$ingal,$back, $c->user->id); push @intel,"Added $&"; }; @@ -371,9 +399,81 @@ sub insertintel : Private { $c->flash(scans => \@scans); } +sub addincs : Local { + my ( $self, $c ) = @_; + $c->stash(incs => $c->flash->{incs}); + +} + +sub postincs : Local { + my ( $self, $c ) = @_; + my $dbh = $c->model; + + my @incs; + + my $user = $dbh->prepare(q{ +SELECT uid FROM users u +WHERE pid = planetid($1,$2,$3,tick()) + AND uid IN (SELECT uid FROM groupmembers WHERE gid = 'M') + }); + my $call = $dbh->prepare(q{ +SELECT call +FROM calls WHERE uid = $1 AND landing_tick = tick() + $2 + }); + my $fleet = $dbh->prepare(q{ +SELECT pid +FROM incomings i +WHERE pid = planetid($1,$2,$3,tick()) AND amount = $4 and fleet = $5 AND call = $6 + }); + my $irc = $dbh->prepare(q{ +INSERT INTO irc_requests (uid,channel,message) VALUES($1,'def',$2) + }); + + my $msg = $c->req->param('message'); + while ($msg =~ m/(\d+):(\d+):(\d+)\*?\s+(\d+):(\d+):(\d+)\*?\s+A\s+(.+?)\s+(Ter|Cat|Xan|Zik|Etd)\s+(\d+)\s+(\d+)/gc + ||$msg =~ /expand\s+(\d+):(\d+):(\d+)\*?\s+(\d+):(\d+):(\d+)\s+([^:]*\S+)\s+(Ter|Cat|Xan|Zik|Etd)\s+([\d,]+)\s+(\d+)/gc + || $msg =~ /(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)\s+\((Ter|Cat|Xan|Zik|Etd)\)\s+([^,]*\S+)\s+([\d,]+)\s+(\d+)\s+\(\d+\)/gc){ + + my $inc = {message => $&}; + my $amount = $9; + { + $amount =~ s/,//g; + } + try { + my $uid = $dbh->selectrow_array($user,undef,$1,$2,$3); + die 'No user with these coords' unless $uid; + + my $call = $dbh->selectrow_array($call,undef,$uid,$10); + if ($call){ + my $pid = $dbh->selectrow_hashref($fleet,undef,$4,$5,$6,$amount,$7,$call); + die 'Duplicate' if $pid; + + } + + my $message = "$1:$2:$3 $4:$5:$6 $7 $8 $amount $10"; + $irc->execute($c->user->id, $message); + $inc->{status} = 'Added'; + + } catch { + when (m(^(.*) at )){ + $inc->{status} = $1; + } + default { + $inc->{status} = $_; + } + }; + push @incs, $inc; + } + + $c->signal_bots if @incs; + $c->flash(incs => \@incs); + $c->res->redirect($c->uri_for('addincs')); +} + sub launchConfirmation : Local { my ( $self, $c ) = @_; + $c->stash(error => $c->flash->{error}); $c->stash(missions => $c->flash->{missions}); } @@ -381,131 +481,373 @@ sub postconfirmation : Local { my ( $self, $c ) = @_; my $dbh = $c->model; - eval { - my $missions = $c->req->param('mission'); - my $findplanet = $dbh->prepare("SELECT planetid(?,?,?,?)"); - my $findattacktarget = $dbh->prepare(q{SELECT c.target,c.wave,c.launched - FROM raid_claims c - JOIN raid_targets t ON c.target = t.id - JOIN raids r ON t.raid = r.id - WHERE c.uid = ? AND r.tick+c.wave-1 = ? AND t.planet = ? - AND r.open AND not r.removed - }); - my $finddefensetarget = $dbh->prepare(q{SELECT c.id FROM calls c - JOIN users u ON c.member = u.uid - WHERE u.planet = $1 AND c.landing_tick = $2 - }); - my $informDefChannel = $dbh->prepare(q{INSERT INTO defense_missions - (fleet,call) VALUES (?,?) - }); - my $addattackpoint = $dbh->prepare(q{UPDATE users SET - attack_points = attack_points + 1 WHERE uid = ? + try { + my $findplanet = $dbh->prepare(q{SELECT planetid(?,?,?,tick())}); + my $addfleet = $dbh->prepare(q{INSERT INTO fleets + (name,mission,pid,tick,amount) + VALUES ($2,$3,(SELECT pid FROM users WHERE uid = $1),tick(),$4) + RETURNING fid }); - my $launchedtarget = $dbh->prepare(q{UPDATE raid_claims SET launched = True - WHERE uid = ? AND target = ? AND wave = ? + my $updatefleet = $dbh->prepare(q{ +UPDATE launch_confirmations SET back = $2 WHERE fid = $1 }); - my $addfleet = $dbh->prepare(q{INSERT INTO fleets - (uid,name,mission,sender,target,tick,eta,back,amount) - VALUES ($1,$2,$3,(SELECT planet FROM users WHERE uid = $1),$4,$5,$6,$7,$8) - RETURNING id + my $addconfirmation = $dbh->prepare(q{INSERT INTO launch_confirmations + (fid,uid,pid,landing_tick,eta,back,num) VALUES ($1,$2,$3,$4,$5,$6,$7) }); - my $addships = $dbh->prepare(q{INSERT INTO fleet_ships (id,ship,amount) + my $addships = $dbh->prepare(q{INSERT INTO fleet_ships (fid,ship,amount) VALUES (?,?,?) }); my $log = $dbh->prepare(q{INSERT INTO forum_posts (ftid,uid,message) VALUES( (SELECT ftid FROM users WHERE uid = $1),$1,$2) }); - my @missions; + my $return = $dbh->prepare(q{ +UPDATE launch_confirmations SET back = tick() +WHERE uid = $1 AND num = $2 AND back > tick() + }); + my $fullfleet = $dbh->prepare(q{INSERT INTO full_fleets + (fid,uid) VALUES (?,?)}); $dbh->begin_work; - while ($missions && $missions =~ m/([^\n]+)\s+(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+) - \s+\((?:(\d+)\+)?(\d+)\).*?(?:\d+hrs\s+)?\d+mins?\s+ - (Attack|Defend|Return|Fake\ Attack|Fake\ Defend) - (.*?) - (?:Launching\ in\ tick\ (\d+),\ arrival\ in\ tick\ (\d+) - |ETA:\ \d+,\ Return\ ETA:\ (\d+) - |Return\ ETA:\ (\d+) - )/sgx){ - next if $10 eq 'Return'; - my %mission; - my $name = $1; - my $tick = $c->stash->{TICK}+$9; - $tick += $8 if defined $8; - my $eta = $9; - my $mission = $10; - my $x = $5; - my $y = $6; - my $z = $7; - my $back = $tick + $eta - 1; - if ($13){ - $tick = $13; - }elsif ($14){ - $back += $14; - } - $mission{tick} = $tick; - $mission{mission} = $mission; - $mission{target} = "$x:$y:$z"; - $mission{back} = $back; - - my ($planet_id) = $dbh->selectrow_array($findplanet,undef,$x,$y,$z,$c->stash->{TICK}); - - my $findtarget = $finddefensetarget; - if ($mission eq 'Attack'){ - $findtarget = $findattacktarget; - $findtarget->execute($c->user->id,$tick,$planet_id); - }elsif ($mission eq 'Defend'){ - $findtarget = $finddefensetarget; - $findtarget->execute($planet_id,$tick); + my @missions = parseconfirmations($c->req->param('mission'), $c->stash->{TICK}); + for my $m (@missions){ + if ($m->{mission} eq 'Return'){ + $c->forward("addReturnFleet", [$m]); + if($m->{fid}){ + $updatefleet->execute($m->{fid},$m->{back}); + next; + }else{ + $m->{pid} = $c->user->planet; + } + }elsif ($m->{target} =~ /^(\d+):(\d+):(\d+)$/) { + $m->{pid} = $dbh->selectrow_array($findplanet,undef,$1,$2,$3); + unless ($m->{pid}){ + $m->{warning} = "No planet at $m->{target}, try again next tick."; + next; + } } - my $ships = $11; - my @ships; - my $amount = 0; - while ($ships =~ m/((?:\w+ )*\w+)\s+\w+\s+(?:(?:\w+|-)\s+){3}(?:Steal|Normal|Emp|Normal\s+Cloaked|Pod|Structure Killer)\s+(\d+)/g){ - $amount += $2; - push @ships,{ship => $1, amount => $2}; + #Recall fleets with same slot number + $return->execute($c->user->id,$m->{num}); + + unless ($m->{mission}){ + $m->{warning} = "Not on a mission, but matching fleets recalled"; + next; } - $mission{ships} = \@ships; - if ($amount == 0){ - warn "No ships in: $ships"; + $c->forward("findDuplicateFleet", [$m]); + if ($m->{match}){ + $m->{warning} = "Already confirmed this fleet, updating changed information"; + $updatefleet->execute($m->{fid},$m->{back}) if $m->{pid}; next; } - my $fleet = $dbh->selectrow_array($addfleet,undef,$c->user->id,$name,$mission - ,$planet_id,$tick,$eta,$back,$amount); - $mission{fleet} = $fleet; - for my $ship (@ships){ - $addships->execute($fleet,$ship->{ship},$ship->{amount}); + + + $m->{fleet} = $dbh->selectrow_array($addfleet,undef,$c->user->id,$m->{name} + ,$m->{mission},$m->{amount}); + + if ($m->{mission} eq 'Full fleet'){ + $fullfleet->execute($m->{fleet},$c->user->id); + }else{ + $addconfirmation->execute($m->{fleet},$c->user->id,$m->{pid},$m->{tick},$m->{eta},$m->{back},$m->{num}); } - if ($findtarget->rows == 0){ - $mission{warning} = 'No matching target!'; - }elsif ($mission eq 'Attack'){ - my $claim = $findtarget->fetchrow_hashref; - if ($claim->{launched}){ - $mission{warning} = "Already launched on this target:$claim->{target},$claim->{wave},$claim->{launched}"; - }else{ - $addattackpoint->execute($c->user->id); - $launchedtarget->execute($c->user->id,$claim->{target},$claim->{wave}); - $mission{warning} = "OK:$claim->{target},$claim->{wave},$claim->{launched}"; - $log->execute($c->user->id,"Gave attack point for confirmation on $mission mission to $x:$y:$z, landing tick $tick"); - } - }elsif ($mission eq 'Defend'){ - my $call = $findtarget->fetchrow_hashref; - $informDefChannel->execute($fleet,$call->{id}); + if ($m->{mission} eq 'Attack'){ + $c->forward("addAttackFleet", [$m]); + }elsif ($m->{mission} eq 'Defend'){ + $c->forward("addDefendFleet", [$m]); } - $log->execute($c->user->id,"Pasted confirmation for $mission mission to $x:$y:$z, landing tick $tick"); - push @missions,\%mission; + for my $ship (@{$m->{ships}}){ + $addships->execute($m->{fleet},$ship->{ship},$ship->{amount}); + } + $log->execute($c->user->id,"Pasted confirmation for $m->{mission} mission to $m->{target}, landing tick $m->{tick}"); } - $dbh->commit; $c->flash(missions => \@missions); - }; - if ($@){ + $dbh->commit; + $c->signal_bots; + } catch { $dbh->rollback; - die $@; + when (/insert or update on table "fleet_ships" violates foreign key constraint "fleet_ships_ship_fkey"\s+DETAIL:\s+Key \(ship\)=\(([^)]+)\)/){ + $c->flash( error => "'$1' is NOT a valid ship"); + } + default{ + $c->flash( error => $_); + } + }; + $c->res->redirect($c->uri_for('launchConfirmation')); +} + +sub parseconfirmations { + my ( $missions, $tick ) = @_; + return unless $missions; + my @slots; + $missions =~ s/\s?,\s?//g; + $missions =~ s/\s*([:+])\s*/$1/g; + $missions =~ s/\(\s/(/g; + $missions =~ s/\s\)/)/g; + my $returnetare = qr/(\d+) \s+ + Arrival:(\d+)/sx; + my $missionetare = qr/(\d+) (\s+ \(\+\d+\))? \s+ + Arrival:(\d+) \s+ + \QReturn ETA:\E\s*(?:(?Instant) \s+ Cancel \s+ Order + | (?\d+) \s+ Ticks \s+ Recall \s+ Fleet)/sx; + my $etare = qr/(Galaxy:\d+Universe:\d+(?:Alliance:\d+)? + |$missionetare + |$returnetare)\s*/x; + my $missre = qr/((?:Fake\ )?\w+)\s*/x; + if ($missions =~ m/ + Ships \s+ Cla \s+ T\s?1 \s+ T\s?2 \s+ T\s?3 \s+ Base \s+ \(i\) \s (?.+?) \s+ \(i\) \s+ (?.+?) \s+ \(i\) \s+ (?.+?) \s+ \(i\) \s+ TOTAL \s+ + (?.+?) + \QTotal Ships in Fleet\E \s+ (\d+) \s+ (?\d+) \s+ (?\d+) \s+ (?\d+) \s+ + Mission: \s* (?(?:$missre)*) \s* + Target: \s* (?((\d+:\d+:\d+)?\s)*) \s* + \QLaunch Tick:\E \s* (?(\d+\s+)*) \s* + ETA: \s* (?(?:$etare)*) + /sx){ + my %match = %-; + my @targets = split /\s+/, $+{targets}; + my @lts = split /\s+/, $+{lts}; + my @etas; + my $_ = $+{etas}; + while(/$etare/sxg){ + push @etas, $1; + } + my @missions ; + $_ = $+{missions}; + while(/$missre/sxg){ + push @missions, $1; + } + for my $i (0..2){ + my %mission = ( + name => $match{name}->[$i], + mission => '' , + amount => $match{amount}->[$i], + num => $i, + ships => [] + ); + if ($mission{amount} == 0){ + push @slots,\%mission; + next; + } + + given(shift @etas){ + when(/$missionetare/sx){ + $mission{tick} = $3; + $mission{eta} = $1 + $+{eta}; + $mission{back} = $3 + $mission{eta} - 1; + $mission{target} = shift @targets; + $mission{lt} = shift @lts; + $mission{mission} = shift @missions; + } + when(/$returnetare/sx){ + $mission{tick} = $2; + $mission{eta} = $1; + $mission{back} = $2; + $mission{target} = shift @targets; + $mission{lt} = shift @lts; + $mission{mission} = shift @missions; + die 'Did you forget some at the end?' if $mission{mission} ne 'Return'; + } + } + push @slots,\%mission; + } + push @slots,{ + name => 'Main', + num => 3, + mission => 'Full fleet', + tick => $tick, + amount => 0, + ships => [] + }; + while ($match{ships}->[0] =~ m/(\w[ \w]+?)\s+(FI|CO|FR|DE|CR|BS)[^\d]+((?:\d+\s*){5})/g){ + my $ship = $1; + my @amounts = split /\D+/, $3; + my $base = shift @amounts; + die "Ships don't sum up properly" if $amounts[3] != $base + $amounts[0] + $amounts[1] + $amounts[2]; + for my $i (0..3){ + push @{$slots[$i]->{ships}},{ship => $ship, amount => $amounts[$i]} if $amounts[$i] > 0; + } + $slots[3]->{amount} += $amounts[3]; + } } + return @slots; +} - $c->res->redirect($c->uri_for('launchConfirmation')); +sub findDuplicateFleet : Private { + my ( $self, $c, $m ) = @_; + my $dbh = $c->model; + + my $findfleet = $dbh->prepare(q{ +SELECT fid FROM fleets f + LEFT JOIN launch_confirmations lc USING (fid) +WHERE f.pid = (SELECT pid FROM users WHERE uid = $1) + AND mission = $3 AND amount = $4 AND (mission <> 'Full fleet' OR tick > $6 - 6) + AND COALESCE(uid = $1 AND num = $2 AND lc.pid = $5 AND landing_tick = $6, TRUE) + }); + my $fid = $dbh->selectrow_array($findfleet,undef,$c->user->id,$m->{num} + ,$m->{mission},$m->{amount}, $m->{pid}, $m->{tick}); + $c->forward("matchShips", [$m,$fid]); + $m->{fid} = $fid if $m->{match}; +} + +sub addAttackFleet : Private { + my ( $self, $c, $m ) = @_; + my $dbh = $c->model; + + my $findattacktarget = $dbh->prepare(q{ +SELECT c.target,c.wave,c.launched +FROM raid_claims c + JOIN raid_targets t ON c.target = t.id + JOIN raids r ON t.raid = r.id +WHERE c.uid = ? AND r.tick+c.wave-1 = ? AND t.pid = ? + AND r.open AND not r.removed + }); + my $launchedtarget = $dbh->prepare(q{ +UPDATE raid_claims SET launched = TRUE +WHERE uid = ? AND target = ? AND wave = ? + }); + my $claim = $dbh->selectrow_hashref($findattacktarget,undef,$c->user->id,$m->{tick},$m->{pid}); + if ($claim->{launched}){ + $m->{warning} = "Already launched on this target:$claim->{target},$claim->{wave},$claim->{launched}"; + }elsif(defined $claim->{launched}){ + $launchedtarget->execute($c->user->id,$claim->{target},$claim->{wave}); + $m->{warning} = "OK:$claim->{target},$claim->{wave},$claim->{launched}"; + }else{ + $m->{warning} = "You haven't claimed this target"; + } +} + +sub addDefendFleet : Private { + my ( $self, $c, $m ) = @_; + my $dbh = $c->model; + + my $finddefensetarget = $dbh->prepare(q{ +SELECT call FROM calls c + JOIN users u USING (uid) +WHERE u.pid = $1 AND c.landing_tick = $2 + }); + my $informDefChannel = $dbh->prepare(q{ +INSERT INTO defense_missions (fleet,call) VALUES (?,?) + }); + my $call = $dbh->selectrow_hashref($finddefensetarget,undef,$m->{pid},$m->{tick}); + if ($call->{call}){ + $informDefChannel->execute($m->{fleet},$call->{call}); + }else{ + $m->{warning} = "No call for $m->{target} landing tick $m->{tick}"; + } +} + +sub addReturnFleet : Private { + my ( $self, $c, $m ) = @_; + my $dbh = $c->model; + + my $findfleet = $dbh->prepare(q{ +SELECT fid FROM fleets f + JOIN launch_confirmations lc USING (fid) +WHERE uid = $1 AND num = $2 AND amount = $3 + AND back >= $4 + }); + my $fid = $dbh->selectrow_array($findfleet,undef,$c->user->id,$m->{num} + ,$m->{amount}, $m->{tick}); + $c->forward("matchShips", [$m,$fid]); + if ($m->{match}){ + $m->{fid} = $fid; + $m->{warning} = "Return fleet, changed back tick to match the return eta."; + } else { + $m->{warning} = "Couldn't find a fleet matching this returning fleet, so adding a new fleet that is returning"; + } +} + +sub matchShips : Private { + my ( $self, $c, $m, $fid ) = @_; + return unless $fid; + my $dbh = $c->model; + + my $ships = $dbh->prepare(q{ +SELECT ship, amount FROM fleet_ships WHERE fid = $1 ORDER BY num + }); + $ships->execute($fid); + for my $s (@{$m->{ships}}){ + my $s2 = $ships->fetchrow_hashref; + return unless $s->{ship} eq $s2->{ship} && $s->{amount} == $s2->{amount}; + } + $m->{match} = 1; + +} + +sub defenders : Local { + my ( $self, $c, $order ) = @_; + my $dbh = $c->model; + + my $defenders = $dbh->prepare(q{ +SELECT uid,pid AS planet,username, to_char(NOW() AT TIME ZONE timezone,'HH24:MI') AS time + ,sms_note, call_if_needed, race, timezone +FROM users u + JOIN current_planet_stats p USING (pid) +WHERE uid IN (SELECT uid FROM groupmembers WHERE gid = 'M') +ORDER BY call_if_needed DESC, username + }); + $defenders->execute; + + my $available = $dbh->prepare(q{ +SELECT ship,amount FROM available_ships WHERE pid = $1 + }); + + my @members; + while (my $member = $defenders->fetchrow_hashref){ + + $member->{fleets} = member_fleets($dbh, $member->{uid}, $member->{planet}); + $available->execute($member->{planet}); + my $fleet = {fid => $member->{username}, mission => 'Available', name => 'At home' + , ships => $available->fetchall_arrayref({}) + }; + push @{$member->{fleets}}, $fleet; + push @members,$member; + } + $c->stash(members => \@members); +} + +sub member_fleets { + my ( $dbh, $uid, $planet ) = @_; + + my $query = $dbh->prepare(q{ +( + SELECT DISTINCT ON (mission,name) fid,name,tick, NULL AS eta + ,amount, NULL AS coords, pid AS target, NULL AS back + ,NULL AS recalled, mission + FROM fleets f + WHERE pid = $2 AND tick <= tick() AND tick >= tick() - 24 + AND name IN ('Main','Advanced Unit') AND mission = 'Full fleet' + ORDER BY mission,name,tick DESC, fid DESC +) UNION ( + SELECT fid,name,landing_tick AS tick, eta, amount + , coords(x,y,z), lc.pid AS target, back + , (back <> landing_tick + eta - 1) AS recalled + ,CASE WHEN landing_tick <= tick() OR (back <> landing_tick + eta - 1) + THEN 'Returning' ELSE mission END AS mission + FROM launch_confirmations lc + LEFT OUTER JOIN current_planet_stats t USING (pid) + JOIN fleets f USING (fid) + WHERE uid = $1 AND f.pid = $2 AND back > tick() + AND landing_tick - eta - 12 < tick() +) + }); + + my $ships = $dbh->prepare(q{SELECT ship,amount FROM fleet_ships + WHERE fid = ? ORDER BY num + }); + + $query->execute($uid,$planet); + my @fleets; + while (my $fleet = $query->fetchrow_hashref){ + my @ships; + $ships->execute($fleet->{fid}); + while (my $ship = $ships->fetchrow_hashref){ + push @ships,$ship; + } + $fleet->{ships} = \@ships; + push @fleets,$fleet; + } + return \@fleets; } =head1 AUTHOR