mysqli=new mysqli(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$this->mysqli->set_charset(“utf8mb4”);
}
public function fetchAll($sql,$params=[]){
$stmt=$this->mysqli->prepare($sql);
if($params){$stmt->bind_param(str_repeat(“s”,count($params)),…$params);}
$stmt->execute();
$res=$stmt->get_result();
$rows=$res?$res->fetch_all(MYSQLI_ASSOC):[];
$stmt->close();
return $rows;
}
public function fetchOne($sql,$params=[]){$rows=$this->fetchAll($sql,$params);return $rows[0]??null;}
}
/* ================= Resolver ================= */
class OpenSimResolver {
private $db;
public function __construct(DB $db){$this->db=$db;}
public function findInventoryObjectsByName($name,$ownerUUID=null){
$sql=”SELECT inventoryID,assetID,inventoryName,assetType,avatarID,creationDate AS created
FROM inventoryitems WHERE assetType=6 AND inventoryName=?”.($ownerUUID?” AND avatarID=?”:””).”
ORDER BY creationDate DESC”;
$params=$ownerUUID?[$name,$ownerUUID]:[$name];
return $this->db->fetchAll($sql,$params);
}
public function findInventoryObjectsByNameLike($name,$ownerUUID=null,$limit=50){
$like=”%”.$name.”%”;
$sql=”SELECT inventoryID,assetID,inventoryName,assetType,avatarID,creationDate AS created
FROM inventoryitems WHERE assetType=6 AND inventoryName LIKE ?”.($ownerUUID?” AND avatarID=?”:””).”
ORDER BY creationDate DESC LIMIT “.intval($limit);
$params=$ownerUUID?[$like,$ownerUUID]:[$like];
return $this->db->fetchAll($sql,$params);
}
public function assetsRow($assetID){return $this->db->fetchOne(“SELECT id,name,assetType FROM assets WHERE id=? LIMIT 1”,[$assetID]);}
}
/* ================= RemoteAdmin ================= */
class OpenSimRemoteAdmin {
private $host,$port,$password;
public function __construct($host,$port,$password){$this->host=$host;$this->port=$port;$this->password=$password;}
public function sendCommand($method,$params=[]){
$params[“password”]=$this->password;
$xml=”
foreach($params as $k=>$v){
$escaped=htmlspecialchars((string)$v,ENT_QUOTES|ENT_XML1,’UTF-8′);
$xml.=”\n
}
$xml.=”\n
$fp=@fsockopen($this->host,$this->port,$errno,$errstr,5);
if(!$fp)return[“ok”=>false,”raw”=>”❌ Connection failed: $errstr ($errno)”];
fwrite($fp,”POST / HTTP/1.1\r\nHost: {$this->host}\r\nContent-Type: text/xml\r\nContent-Length: “.strlen($xml).”\r\nConnection: close\r\n\r\n$xml”);
$resp=””;while(!feof($fp))$resp.=fgets($fp,8192);fclose($fp);
$msg=$err=null;
if(preg_match(‘/
if(preg_match(‘/
if($err)return[“ok”=>false,”raw”=>$resp,”error”=>$err];
if($msg)return[“ok”=>true,”raw”=>$resp,”message”=>$msg];
return[“ok”=>false,”raw”=>$resp,”error”=>”Unknown response”];
}
}
/* ================= Router ================= */
try {
$method=$_SERVER[‘REQUEST_METHOD’]??’GET’;
$action=$_GET[‘action’]??$_POST[‘action’]??”;
if($action===’search’ && $method===’GET’){
$item=trim($_GET[‘item_name’]??DEFAULT_ITEM_NAME);
$owner=trim($_GET[‘owner’]??DEFAULT_OWNER);
$db=new DB();$resolver=new OpenSimResolver($db);
$rows=$resolver->findInventoryObjectsByName($item,$owner);
if(empty($rows))$rows=$resolver->findInventoryObjectsByNameLike($item,$owner);
$results=array_map(fn($r)=>[
“inventoryID”=>$r[“inventoryID”],
“assetID”=>$r[“assetID”],
“inventoryName”=>$r[“inventoryName”],
“created”=>$r[“created”],
“created_human”=>humanTime($r[“created”])
],$rows);
json_out([“ok”=>true,”count”=>count($results),”results”=>$results]);
}
if($action===’rez’ && $method===’POST’){
$body=get_json_body(); if($body===null)$body=$_POST;
$asset=trim($body[‘assetid’]??”);
$itemNm=trim($body[‘item_name’]??”);
$owner=trim($body[‘owner’]??DEFAULT_OWNER);
$region=trim($body[‘region’]??DEFAULT_REGION);
$x=trim((string)($body[‘x’]??DEFAULT_X));
$y=trim((string)($body[‘y’]??DEFAULT_Y));
$z=trim((string)($body[‘z’]??DEFAULT_Z));
$db=new DB();$resolver=new OpenSimResolver($db);
if($asset===”){
$nameToFind=$itemNm!==”?$itemNm:DEFAULT_ITEM_NAME;
$matches=$resolver->findInventoryObjectsByName($nameToFind,$owner);
if(empty($matches))$matches=$resolver->findInventoryObjectsByNameLike($nameToFind,$owner);
if(empty($matches))json_out([“ok”=>false,”error”=>”Item not found (name=’$nameToFind’, owner=’$owner’).”],404);
$low=strtolower($nameToFind);
$chosen=null;foreach($matches as $m){if(strtolower($m[‘inventoryName’])===$low){$chosen=$m;break;}}
if(!$chosen)$chosen=$matches[0];
$asset=$chosen[‘assetID’]??”;
if($asset===”)json_out([“ok”=>false,”error”=>”Resolved item has no assetID.”],422);
}
$assetRow=$resolver->assetsRow($asset);
$client=new OpenSimRemoteAdmin(RA_HOST,RA_PORT,RA_PASS);
$res=$client->sendCommand(“admin_rez_asset_persistent”,[
“region”=>$region,”assetid”=>$asset,”owner”=>$owner,”x”=>$x,”y”=>$y,”z”=>$z
]);
$payload=$res+[“asset_meta”=>$assetRow,”region”=>$region,”owner”=>$owner,”x”=>$x,”y”=>$y,”z”=>$z];
json_out($payload,$res[“ok”]?200:500);
}
json_out([“ok”=>false,”error”=>”Bad request. Use GET action=search or POST action=rez.”],400);
}catch(Throwable $e){json_out([“ok”=>false,”error”=>”Server error: “.$e->getMessage()],500);}