mirror of
https://github.com/Shadowss/TravianZ.git
synced 2026-09-01 16:17:13 +00:00
Fix parallel building exploit via race condition (#141)
Co-authored-by: hdmaniak <bombo15.forum@gmail.com>
This commit is contained in:
+116
-91
@@ -309,6 +309,9 @@ class Building {
|
|||||||
|
|
||||||
public function loadBuilding() {
|
public function loadBuilding() {
|
||||||
global $database,$village,$session;
|
global $database,$village,$session;
|
||||||
|
$this->basic = 0;
|
||||||
|
$this->inner = 0;
|
||||||
|
$this->plus = 0;
|
||||||
$this->buildArray = $database->getJobs($village->wid);
|
$this->buildArray = $database->getJobs($village->wid);
|
||||||
$this->allocated = count($this->buildArray);
|
$this->allocated = count($this->buildArray);
|
||||||
if($this->allocated > 0) {
|
if($this->allocated > 0) {
|
||||||
@@ -345,130 +348,152 @@ class Building {
|
|||||||
private function upgradeBuilding($id) {
|
private function upgradeBuilding($id) {
|
||||||
global $database, $village, $session, $logging, ${'bid'.$village->resarray['f'.$id.'t']};
|
global $database, $village, $session, $logging, ${'bid'.$village->resarray['f'.$id.'t']};
|
||||||
|
|
||||||
if($this->allocated < $this->maxConcurrent) {
|
if(!$database->getBuildLock($village->wid)) return;
|
||||||
$uprequire = $this->resourceRequired($id,$village->resarray['f'.$id.'t']);
|
try {
|
||||||
$time = time() + $uprequire['time'];
|
$this->loadBuilding();
|
||||||
$bindicate = $this->canBuild($id,$village->resarray['f'.$id.'t']);
|
|
||||||
|
|
||||||
// don't allow building above max levels and don't allow building if it's in demolition
|
if($this->allocated < $this->maxConcurrent) {
|
||||||
if (in_array($bindicate, [1, 2, 3, 10, 11])) {
|
$uprequire = $this->resourceRequired($id,$village->resarray['f'.$id.'t']);
|
||||||
header("Location: dorf2.php");
|
$time = time() + $uprequire['time'];
|
||||||
exit;
|
$bindicate = $this->canBuild($id,$village->resarray['f'.$id.'t']);
|
||||||
}
|
|
||||||
|
|
||||||
$loop = ($bindicate == 9 ? 1 : 0);
|
// don't allow building above max levels and don't allow building if it's in demolition
|
||||||
$loopsame = 0;
|
if (in_array($bindicate, [1, 2, 3, 10, 11])) {
|
||||||
if($loop == 1) {
|
$database->releaseBuildLock($village->wid);
|
||||||
foreach($this->buildArray as $build) {
|
header("Location: dorf2.php");
|
||||||
if($build['field'] == $id) {
|
exit;
|
||||||
$loopsame++;
|
|
||||||
$uprequire = $this->resourceRequired($id, $village->resarray['f'.$id.'t'], ($loopsame > 0 ? 2 : 1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if($session->tribe == 1 || ALLOW_ALL_TRIBE) {
|
|
||||||
if($id >= 19) {
|
$loop = ($bindicate == 9 ? 1 : 0);
|
||||||
foreach($this->buildArray as $build) {
|
$loopsame = 0;
|
||||||
if($build['field'] >= 19) {
|
if($loop == 1) {
|
||||||
$time = $build['timestamp'] + $uprequire['time'];
|
foreach($this->buildArray as $build) {
|
||||||
|
if($build['field'] == $id) {
|
||||||
|
$loopsame++;
|
||||||
|
$uprequire = $this->resourceRequired($id, $village->resarray['f'.$id.'t'], ($loopsame > 0 ? 2 : 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($session->tribe == 1 || ALLOW_ALL_TRIBE) {
|
||||||
|
if($id >= 19) {
|
||||||
|
foreach($this->buildArray as $build) {
|
||||||
|
if($build['field'] >= 19) {
|
||||||
|
$time = $build['timestamp'] + $uprequire['time'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foreach($this->buildArray as $build) {
|
||||||
|
if($build['field'] <= 18) {
|
||||||
|
$time = $build['timestamp'] + $uprequire['time'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
foreach($this->buildArray as $build) {
|
$time = $this->buildArray[0]['timestamp'] + $uprequire['time'];
|
||||||
if($build['field'] <= 18) {
|
|
||||||
$time = $build['timestamp'] + $uprequire['time'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
$level = $database->getResourceLevel($village->wid);
|
||||||
$time = $this->buildArray[0]['timestamp'] + $uprequire['time'];
|
|
||||||
|
|
||||||
|
if($database->addBuilding($village->wid, $id, $village->resarray['f'.$id.'t'], $loop, $time + ($loop == 1 ? ceil(60 / SPEED) : 0), 0, $level['f'.$id] + 1 + count($database->getBuildingByField($village->wid, $id)))) {
|
||||||
|
$database->modifyResource($village->wid, $uprequire['wood'], $uprequire['clay'], $uprequire['iron'], $uprequire['crop'], 0);
|
||||||
|
$logging->addBuildLog($village->wid, self::procResType($village->resarray['f'.$id.'t']), ($village->resarray['f'.$id] + ($loopsame > 0 ? 2 : 1)), 0);
|
||||||
|
$this->redirect($id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$level = $database->getResourceLevel($village->wid);
|
} finally {
|
||||||
|
$database->releaseBuildLock($village->wid);
|
||||||
|
|
||||||
if($database->addBuilding($village->wid, $id, $village->resarray['f'.$id.'t'], $loop, $time + ($loop == 1 ? ceil(60 / SPEED) : 0), 0, $level['f'.$id] + 1 + count($database->getBuildingByField($village->wid, $id)))) {
|
|
||||||
$database->modifyResource($village->wid, $uprequire['wood'], $uprequire['clay'], $uprequire['iron'], $uprequire['crop'], 0);
|
|
||||||
$logging->addBuildLog($village->wid, self::procResType($village->resarray['f'.$id.'t']), ($village->resarray['f'.$id] + ($loopsame > 0 ? 2 : 1)), 0);
|
|
||||||
$this->redirect($id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function downgradeBuilding($id) {
|
private function downgradeBuilding($id) {
|
||||||
global $database, $village, $session, $logging;
|
global $database, $village, $session, $logging;
|
||||||
|
|
||||||
if($this->allocated < $this->maxConcurrent) {
|
if(!$database->getBuildLock($village->wid)) return;
|
||||||
$name = "bid".$village->resarray['f'.$id.'t'];
|
try {
|
||||||
global $$name;
|
$this->loadBuilding();
|
||||||
$dataarray = $$name;
|
|
||||||
$time = time() + round($dataarray[$village->resarray['f'.$id]-1]['time'] / 4);
|
if($this->allocated < $this->maxConcurrent) {
|
||||||
$loop = 0;
|
$name = "bid".$village->resarray['f'.$id.'t'];
|
||||||
if($this->inner == 1 || $this->basic == 1) {
|
global $$name;
|
||||||
if(($session->plus || $village->resarray['f'.$id.'t']==40)&& $this->plus == 0) {
|
$dataarray = $$name;
|
||||||
$loop = 1;
|
$time = time() + round($dataarray[$village->resarray['f'.$id]-1]['time'] / 4);
|
||||||
|
$loop = 0;
|
||||||
|
if($this->inner == 1 || $this->basic == 1) {
|
||||||
|
if(($session->plus || $village->resarray['f'.$id.'t']==40)&& $this->plus == 0) {
|
||||||
|
$loop = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if($loop == 1) {
|
||||||
if($loop == 1) {
|
if($session->tribe == 1 || ALLOW_ALL_TRIBE) {
|
||||||
if($session->tribe == 1 || ALLOW_ALL_TRIBE) {
|
if($id >= 19) {
|
||||||
if($id >= 19) {
|
foreach($this->buildArray as $build) {
|
||||||
foreach($this->buildArray as $build) {
|
if($build['field'] >= 19) {
|
||||||
if($build['field'] >= 19) {
|
$time = $build['timestamp'] + round($dataarray[$village->resarray['f'.$id]-1]['time'] / 4);
|
||||||
$time = $build['timestamp'] + round($dataarray[$village->resarray['f'.$id]-1]['time'] / 4);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
$time = $this->buildArray[0]['timestamp'] + round($dataarray[$village->resarray['f'.$id]-1]['time'] / 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
$time = $this->buildArray[0]['timestamp'] + round($dataarray[$village->resarray['f'.$id]-1]['time'] / 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$level = $database->getResourceLevel($village->wid);
|
$level = $database->getResourceLevel($village->wid);
|
||||||
if($database->addBuilding($village->wid, $id, $village->resarray['f'.$id.'t'], $loop, $time, 0, 0, $level['f'.$id] + 1 + count($database->getBuildingByField($village->wid, $id)))){
|
if($database->addBuilding($village->wid, $id, $village->resarray['f'.$id.'t'], $loop, $time, 0, 0, $level['f'.$id] + 1 + count($database->getBuildingByField($village->wid, $id)))){
|
||||||
$logging->addBuildLog($village->wid, self::procResType($village->resarray['f'.$id.'t']), ($village->resarray['f'.$id] - 1), 2);
|
$logging->addBuildLog($village->wid, self::procResType($village->resarray['f'.$id.'t']), ($village->resarray['f'.$id] - 1), 2);
|
||||||
header("Location: dorf2.php");
|
header("Location: dorf2.php");
|
||||||
exit();
|
exit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
$database->releaseBuildLock($village->wid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function constructBuilding($id, $tid) {
|
private function constructBuilding($id, $tid) {
|
||||||
global $database, $village, $session, $logging;
|
global $database, $village, $session, $logging;
|
||||||
|
|
||||||
if($this->allocated < $this->maxConcurrent) {
|
if(!$database->getBuildLock($village->wid)) return;
|
||||||
if($tid == 16) $id = 39;
|
try {
|
||||||
elseif($tid == 31 || $tid == 32 || $tid == 33) $id = 40;
|
$this->loadBuilding();
|
||||||
|
|
||||||
$uprequire = $this->resourceRequired($id, $tid);
|
if($this->allocated < $this->maxConcurrent) {
|
||||||
$time = time() + $uprequire['time'];
|
if($tid == 16) $id = 39;
|
||||||
$bindicate = $this->canBuild($id, $tid);
|
elseif($tid == 31 || $tid == 32 || $tid == 33) $id = 40;
|
||||||
$loop = ($bindicate == 9 ? 1 : 0);
|
|
||||||
|
|
||||||
if($loop == 1) {
|
$uprequire = $this->resourceRequired($id, $tid);
|
||||||
foreach( $this->buildArray as $build) {
|
$time = time() + $uprequire['time'];
|
||||||
if($build['field'] >= 19 || ($session->tribe <> 1 && !ALLOW_ALL_TRIBE)) {
|
$bindicate = $this->canBuild($id, $tid);
|
||||||
$time = $build['timestamp'] + ceil(60 / SPEED) + $uprequire['time'];
|
$loop = ($bindicate == 9 ? 1 : 0);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($this->meetRequirement($tid)) {
|
if($loop == 1) {
|
||||||
$level = $database->getResourceLevel($village->wid);
|
foreach( $this->buildArray as $build) {
|
||||||
if($database->addBuilding($village->wid, $id, $tid, $loop, $time, 0, $level['f' . $id] + 1 + count($database->getBuildingByField($village->wid, $id)))){
|
if($build['field'] >= 19 || ($session->tribe <> 1 && !ALLOW_ALL_TRIBE)) {
|
||||||
$logging->addBuildLog($village->wid, self::procResType($tid), ($village->resarray['f' . $id] + 1), 1);
|
$time = $build['timestamp'] + ceil(60 / SPEED) + $uprequire['time'];
|
||||||
$database->modifyResource($village->wid, $uprequire['wood'], $uprequire['clay'], $uprequire['iron'], $uprequire['crop'], 0);
|
}
|
||||||
header("Location: dorf2.php");
|
}
|
||||||
exit;
|
}
|
||||||
}
|
|
||||||
}else{
|
if($this->meetRequirement($tid)) {
|
||||||
header("location: dorf2.php");
|
$level = $database->getResourceLevel($village->wid);
|
||||||
exit;
|
if($database->addBuilding($village->wid, $id, $tid, $loop, $time, 0, $level['f' . $id] + 1 + count($database->getBuildingByField($village->wid, $id)))){
|
||||||
}
|
$logging->addBuildLog($village->wid, self::procResType($tid), ($village->resarray['f' . $id] + 1), 1);
|
||||||
}else{
|
$database->modifyResource($village->wid, $uprequire['wood'], $uprequire['clay'], $uprequire['iron'], $uprequire['crop'], 0);
|
||||||
header("Location: dorf2.php");
|
header("Location: dorf2.php");
|
||||||
exit;
|
exit;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
header("location: dorf2.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
header("Location: dorf2.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
$database->releaseBuildLock($village->wid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4556,6 +4556,18 @@ References: User ID/Message ID, Mode
|
|||||||
return mysqli_query($this->dblink,$q);
|
return mysqli_query($this->dblink,$q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBuildLock($wid) {
|
||||||
|
$wid = (int) $wid;
|
||||||
|
$result = mysqli_query($this->dblink, "SELECT GET_LOCK('build_village_$wid', 10) AS locked");
|
||||||
|
$row = mysqli_fetch_assoc($result);
|
||||||
|
return $row['locked'] == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function releaseBuildLock($wid) {
|
||||||
|
$wid = (int) $wid;
|
||||||
|
mysqli_query($this->dblink, "SELECT RELEASE_LOCK('build_village_$wid')");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the time required to build a specified building
|
* Get the time required to build a specified building
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user