You've already forked Atomcms-edit
1104 lines
36 KiB
PHP
Executable File
1104 lines
36 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class CatalogService
|
|
{
|
|
private const array CATALOG_STRUCTURE = [
|
|
'Meubels' => [
|
|
'Stoelen' => ['chair'],
|
|
'Tafels' => ['table'],
|
|
'Kasten' => ['shelf'],
|
|
'Bedden' => ['bed'],
|
|
'Banken' => [],
|
|
'Vloerkleden' => ['rug'],
|
|
'Verlichting' => ['lighting'],
|
|
'Vloeren' => ['floor'],
|
|
'Wanddecoratie' => ['wall_decoration', 'window'],
|
|
'Ruimteverdelers' => ['divider'],
|
|
],
|
|
'Spellen' => [
|
|
'Spellen' => ['games'],
|
|
'Verkoopautomaten' => ['vending_machine'],
|
|
'Fortuna' => ['fortuna'],
|
|
],
|
|
'Huisdieren' => [
|
|
'Huisdieren' => ['pets'],
|
|
],
|
|
'Muziek' => [
|
|
'Muziek' => ['music'],
|
|
'Geluidseffecten' => ['sound_fx'],
|
|
],
|
|
'Teleporters' => [
|
|
'Teleporters' => ['teleport'],
|
|
'Poorten' => ['gate'],
|
|
'Rollers' => ['roller'],
|
|
],
|
|
'Wired' => [
|
|
'Wired' => ['wired'],
|
|
'Wired Effecten' => ['wired_effect'],
|
|
'Wired Condities' => ['wired_condition'],
|
|
'Wired Triggers' => ['wired_trigger'],
|
|
'Wired Add-ons' => ['wired_add_on'],
|
|
],
|
|
'Diverse' => [
|
|
'Voedsel' => ['food'],
|
|
'Prijzen' => ['trophy', 'leaderboards'],
|
|
'Cadeaus' => ['present'],
|
|
'Credits' => ['credit'],
|
|
'Tentjes' => ['tent'],
|
|
'Overige' => ['other', 'extras', 'unknown'],
|
|
],
|
|
'Muur & Vloer' => [
|
|
'Muren' => ['wallpaper'],
|
|
'Vloeren' => ['floor_pattern'],
|
|
'Landschappen' => ['landscape'],
|
|
'Dimmers' => ['dimmer'],
|
|
],
|
|
];
|
|
|
|
public function createCatalogPages(): array
|
|
{
|
|
$created = 0;
|
|
|
|
$mainPages = [
|
|
'Meubels' => 1,
|
|
'Spellen' => 2,
|
|
'Huisdieren' => 3,
|
|
'Muziek' => 4,
|
|
'Teleporters' => 5,
|
|
'Wired' => 6,
|
|
'Diverse' => 7,
|
|
'Muur & Vloer' => 8,
|
|
];
|
|
|
|
foreach ($mainPages as $mainName => $order) {
|
|
$this->createMainPage($mainName, $order);
|
|
$created++;
|
|
}
|
|
|
|
$structure = [
|
|
'Meubels' => [
|
|
'Stoelen' => 'chair',
|
|
'Tafels' => 'table',
|
|
'Kasten' => 'shelf',
|
|
'Bedden' => 'bed',
|
|
'Vloerkleden' => 'rug',
|
|
'Verlichting' => 'lighting',
|
|
'Vloeren' => 'floor',
|
|
'Wanddecoratie' => 'wall_decoration',
|
|
'Ruimteverdelers' => 'divider',
|
|
],
|
|
'Spellen' => [
|
|
'Spellen' => 'games',
|
|
'Verkoopautomaten' => 'vending_machine',
|
|
'Fortuna' => 'fortuna',
|
|
],
|
|
'Huisdieren' => [
|
|
'Huisdieren' => 'pets',
|
|
],
|
|
'Muziek' => [
|
|
'Muziek' => 'music',
|
|
'Geluidseffecten' => 'sound_fx',
|
|
],
|
|
'Teleporters' => [
|
|
'Teleporters' => 'teleport',
|
|
'Poorten' => 'gate',
|
|
'Rollers' => 'roller',
|
|
],
|
|
'Wired' => [
|
|
'Wired' => 'wired',
|
|
'Wired Effecten' => 'wired_effect',
|
|
'Wired Condities' => 'wired_condition',
|
|
'Wired Triggers' => 'wired_trigger',
|
|
'Wired Add-ons' => 'wired_add_on',
|
|
],
|
|
'Diverse' => [
|
|
'Voedsel' => 'food',
|
|
'Prijzen' => 'trophy',
|
|
'Cadeaus' => 'present',
|
|
'Credits' => 'credit',
|
|
'Tentjes' => 'tent',
|
|
'Overige' => 'other',
|
|
],
|
|
'Muur & Vloer' => [
|
|
'Muren' => 'wallpaper',
|
|
'Vloeren' => 'floor_pattern',
|
|
'Landschappen' => 'landscape',
|
|
'Dimmers' => 'dimmer',
|
|
],
|
|
];
|
|
|
|
$created = 0;
|
|
$parentPages = [
|
|
'Meubels' => 2,
|
|
'Spellen' => 3,
|
|
'Huisdieren' => 4,
|
|
'Muziek' => 5,
|
|
'Teleporters' => 6,
|
|
'Wired' => 7,
|
|
'Diverse' => 8,
|
|
'Muur & Vloer' => 9,
|
|
];
|
|
|
|
foreach ($structure as $mainName => $subPages) {
|
|
$parentId = $parentPages[$mainName] ?? 1;
|
|
$order = 1;
|
|
|
|
foreach (array_keys($subPages) as $subName) {
|
|
$id = $this->createSubPage($subName, $parentId, $order++);
|
|
$created++;
|
|
}
|
|
}
|
|
|
|
return ['pages_created' => $created];
|
|
}
|
|
|
|
public function getFurnitureData(): array
|
|
{
|
|
$path = base_path('../Gamedata/config/FurnitureData.json');
|
|
if (! file_exists($path)) {
|
|
$path = '/var/www/Gamedata/config/FurnitureData.json';
|
|
}
|
|
if (! file_exists($path)) {
|
|
$path = base_path('public/gamedata/config/FurnitureData.json');
|
|
}
|
|
|
|
$data = json_decode(file_get_contents($path), true);
|
|
|
|
return $data['roomitemtypes']['furnitype'] ?? [];
|
|
}
|
|
|
|
private function createMainPage(string $name, int $order): int
|
|
{
|
|
$exists = DB::table('catalog_pages')->where('caption', $name)->first();
|
|
if ($exists) {
|
|
return $exists->id;
|
|
}
|
|
|
|
return DB::table('catalog_pages')->insertGetId([
|
|
'caption' => $name,
|
|
'caption_save' => $name,
|
|
'parent_id' => -1,
|
|
'visible' => 1,
|
|
'min_rank' => 1,
|
|
'icon_image' => 0,
|
|
'order_num' => $order,
|
|
'page_layout' => 'default_3x3',
|
|
]);
|
|
}
|
|
|
|
private function createSubPage(string $name, int $parentId, int $order): int
|
|
{
|
|
$exists = DB::table('catalog_pages')->where('caption', $name)->where('parent_id', $parentId)->first();
|
|
if ($exists) {
|
|
return $exists->id;
|
|
}
|
|
|
|
return DB::table('catalog_pages')->insertGetId([
|
|
'caption' => $name,
|
|
'caption_save' => $name,
|
|
'parent_id' => $parentId,
|
|
'visible' => 1,
|
|
'min_rank' => 1,
|
|
'icon_image' => 0,
|
|
'order_num' => $order,
|
|
'page_layout' => 'default_3x3',
|
|
]);
|
|
}
|
|
|
|
public function rebuildCatalog(): array
|
|
{
|
|
Log::info('[CatalogService] Starting full catalog rebuild');
|
|
|
|
DB::table('catalog_pages')->where('parent_id', '>', 0)->delete();
|
|
DB::table('catalog_pages')->whereIn('parent_id', [-1, 0])->where('id', '>', 10)->delete();
|
|
|
|
$furnitureData = $this->getFurnitureData();
|
|
|
|
$itemIdToCategory = [];
|
|
foreach ($furnitureData as $item) {
|
|
$id = $item['id'];
|
|
if (is_numeric($id)) {
|
|
$itemIdToCategory[$id] = $item['category'] ?? 'unknown';
|
|
}
|
|
}
|
|
|
|
$mainOrder = 1;
|
|
$categoryToPageId = [];
|
|
|
|
foreach (self::CATALOG_STRUCTURE as $mainName => $subCategories) {
|
|
$mainPageId = $this->createMainPage($mainName, $mainOrder++);
|
|
|
|
$subOrder = 1;
|
|
foreach ($subCategories as $subName => $categories) {
|
|
$subPageId = $this->createSubPage($subName, $mainPageId, $subOrder++);
|
|
|
|
foreach ($categories as $cat) {
|
|
$categoryToPageId[$cat] = $subPageId;
|
|
}
|
|
}
|
|
}
|
|
|
|
$updated = 0;
|
|
foreach ($itemIdToCategory as $itemId => $category) {
|
|
if (isset($categoryToPageId[$category])) {
|
|
DB::table('catalog_items')
|
|
->where('item_ids', (string) $itemId)
|
|
->update(['page_id' => $categoryToPageId[$category]]);
|
|
$updated++;
|
|
}
|
|
}
|
|
|
|
Log::info('[CatalogService] Rebuild complete', ['items' => $updated]);
|
|
|
|
return [
|
|
'items_organized' => $updated,
|
|
'pages_created' => DB::table('catalog_pages')->count(),
|
|
];
|
|
}
|
|
|
|
public function organizeCatalog(): array
|
|
{
|
|
Log::info('[CatalogService] Starting fast catalog organization');
|
|
|
|
$categoryToPageId = [
|
|
'chair' => 2221,
|
|
'table' => 2222,
|
|
'shelf' => 2223,
|
|
'bed' => 2224,
|
|
'rug' => 2226,
|
|
'floor' => 2228,
|
|
'lighting' => 2227,
|
|
'wall_decoration' => 2229,
|
|
'divider' => 2230,
|
|
'teleport' => 2240,
|
|
'gate' => 2241,
|
|
'roller' => 2242,
|
|
'wired' => 2244,
|
|
'wired_effect' => 2245,
|
|
'wired_condition' => 2246,
|
|
'wired_trigger' => 2247,
|
|
'wired_add_on' => 2248,
|
|
'music' => 2237,
|
|
'sound_fx' => 2238,
|
|
'vending_machine' => 2233,
|
|
'games' => 2232,
|
|
'fortuna' => 2234,
|
|
'pets' => 2235,
|
|
'food' => 2250,
|
|
'trophy' => 2251,
|
|
'present' => 2252,
|
|
'credit' => 2253,
|
|
'tent' => 2254,
|
|
'other' => 'other',
|
|
'leaderboards' => 2251,
|
|
'extras' => 'other',
|
|
'window' => 2229,
|
|
'wallpaper' => 'wallpaper',
|
|
'floor_pattern' => 'floor_pattern',
|
|
'landscape' => 'landscape',
|
|
'dimmer' => 'dimmer',
|
|
];
|
|
|
|
$furnitureData = $this->getFurnitureData();
|
|
|
|
$updates = [];
|
|
$categoryCounts = [];
|
|
|
|
foreach ($furnitureData as $item) {
|
|
$id = $item['id'];
|
|
$category = $item['category'] ?? 'unknown';
|
|
|
|
if (! is_numeric($id)) {
|
|
continue;
|
|
}
|
|
|
|
$pageId = $categoryToPageId[$category] ?? null;
|
|
|
|
if ($pageId && is_numeric($pageId)) {
|
|
$updates[$pageId][] = (int) $id;
|
|
$categoryCounts[$category] = ($categoryCounts[$category] ?? 0) + 1;
|
|
}
|
|
}
|
|
|
|
$totalUpdated = 0;
|
|
foreach ($updates as $pageId => $itemIds) {
|
|
if ($itemIds === []) {
|
|
continue;
|
|
}
|
|
|
|
$chunks = array_chunk($itemIds, 500);
|
|
foreach ($chunks as $chunk) {
|
|
$chunkStrings = array_map(fn ($id) => (string) $id, $chunk);
|
|
DB::table('catalog_items')
|
|
->whereIn('item_ids', $chunkStrings)
|
|
->update(['page_id' => $pageId]);
|
|
$totalUpdated += count($chunk);
|
|
}
|
|
}
|
|
|
|
Log::info('[CatalogService] Fast organization complete', ['items' => $totalUpdated]);
|
|
|
|
return [
|
|
'items_organized' => $totalUpdated,
|
|
];
|
|
}
|
|
|
|
public function importFromUrl(string $baseUrl): array
|
|
{
|
|
Log::info('[CatalogService] Starting import from URL: ' . $baseUrl);
|
|
|
|
$results = [
|
|
'furniture' => 0,
|
|
'product' => 0,
|
|
'icons' => 0,
|
|
'catalog_images' => 0,
|
|
'errors' => [],
|
|
];
|
|
|
|
// 1. Download FurnitureData.json
|
|
$furnitureUrl = $baseUrl . '/gamedata/config/FurnitureData.json';
|
|
$localPath = '/var/www/Gamedata/config/FurnitureData.json';
|
|
|
|
try {
|
|
$content = @file_get_contents($furnitureUrl);
|
|
if ($content && json_decode($content, true)) {
|
|
file_put_contents($localPath, $content);
|
|
$data = json_decode($content, true);
|
|
$results['furniture'] = count($data['roomitemtypes']['furnitype'] ?? []);
|
|
Log::info('[CatalogService] Downloaded FurnitureData.json');
|
|
} else {
|
|
$results['errors'][] = 'FurnitureData.json niet gevonden of ongeldig';
|
|
}
|
|
} catch (Exception $e) {
|
|
$results['errors'][] = 'FurnitureData: ' . $e->getMessage();
|
|
}
|
|
|
|
// 2. Download ProductData.json
|
|
$productUrl = $baseUrl . '/gamedata/config/ProductData.json';
|
|
|
|
try {
|
|
$content = @file_get_contents($productUrl);
|
|
if ($content && json_decode($content, true)) {
|
|
file_put_contents('/var/www/Gamedata/config/ProductData.json', $content);
|
|
$data = json_decode($content, true);
|
|
$results['product'] = count($data['productdata'] ?? []);
|
|
Log::info('[CatalogService] Downloaded ProductData.json');
|
|
} else {
|
|
$results['errors'][] = 'ProductData.json niet gevonden of ongeldig';
|
|
}
|
|
} catch (Exception $e) {
|
|
$results['errors'][] = 'ProductData: ' . $e->getMessage();
|
|
}
|
|
|
|
// 3. Download icons
|
|
$results['icons'] = $this->downloadIconsFromUrl($baseUrl);
|
|
|
|
// 4. Download catalogue images
|
|
$results['catalog_images'] = $this->downloadCatalogImagesFromUrl($baseUrl);
|
|
|
|
return $results;
|
|
}
|
|
|
|
private function downloadIconsFromUrl(string $baseUrl): int
|
|
{
|
|
$downloaded = 0;
|
|
$iconsPath = '/var/www/Gamedata/icons';
|
|
$url = $baseUrl . '/gamedata/icons';
|
|
|
|
// Try to list files via HTTP (may not work)
|
|
// For now, we'll try common icon names from FurnitureData
|
|
$furnitureData = $this->getFurnitureData();
|
|
|
|
$iconNames = [];
|
|
foreach ($furnitureData as $item) {
|
|
if (isset($item['assets']['asset'])) {
|
|
foreach ($item['assets']['asset'] as $asset) {
|
|
if (isset($asset['name']) && str_contains($asset['name'], '_icon')) {
|
|
$iconNames[] = $asset['name'] . '.png';
|
|
}
|
|
}
|
|
}
|
|
// Also check for furni icon
|
|
if (isset($item['furni']['id'])) {
|
|
$iconNames[] = $item['classname'] . '_icon.png';
|
|
}
|
|
}
|
|
|
|
$iconNames = array_unique($iconNames);
|
|
|
|
foreach (array_slice($iconNames, 0, 500) as $iconName) {
|
|
$iconUrl = $url . '/' . $iconName;
|
|
$localFile = $iconsPath . '/' . $iconName;
|
|
|
|
if (! file_exists($localFile)) {
|
|
try {
|
|
$content = @file_get_contents($iconUrl);
|
|
if ($content) {
|
|
file_put_contents($localFile, $content);
|
|
$downloaded++;
|
|
}
|
|
} catch (Exception) {
|
|
// Skip failed downloads
|
|
}
|
|
}
|
|
}
|
|
|
|
Log::info('[CatalogService] Downloaded ' . $downloaded . ' icons');
|
|
|
|
return $downloaded;
|
|
}
|
|
|
|
private function downloadCatalogImagesFromUrl(string $baseUrl): int
|
|
{
|
|
$downloaded = 0;
|
|
$catalogPath = '/var/www/Gamedata/catalogue';
|
|
$url = $baseUrl . '/gamedata/catalogue';
|
|
|
|
// Download common catalogue images
|
|
$commonImages = [
|
|
'header.png', 'footer.png', 'info.png', 'past.png',
|
|
'club_header.png', 'club_past.png', 'vip_header.png',
|
|
];
|
|
|
|
foreach ($commonImages as $image) {
|
|
$imageUrl = $url . '/' . $image;
|
|
$localFile = $catalogPath . '/' . $image;
|
|
|
|
if (! file_exists($localFile)) {
|
|
try {
|
|
$content = @file_get_contents($imageUrl);
|
|
if ($content) {
|
|
file_put_contents($localFile, $content);
|
|
$downloaded++;
|
|
}
|
|
} catch (Exception) {
|
|
// Skip failed downloads
|
|
}
|
|
}
|
|
}
|
|
|
|
Log::info('[CatalogService] Downloaded ' . $downloaded . ' catalog images');
|
|
|
|
return $downloaded;
|
|
}
|
|
|
|
public function generateCatalogSql(): array
|
|
{
|
|
$furnitureData = $this->getFurnitureData();
|
|
|
|
// Get current catalog item IDs
|
|
$existingIds = DB::table('catalog_items')
|
|
->pluck('item_ids')
|
|
->map(fn ($id) => (int) $id)
|
|
->toArray();
|
|
|
|
$newItems = [];
|
|
foreach ($furnitureData as $item) {
|
|
$id = (int) $item['id'];
|
|
if (! in_array($id, $existingIds)) {
|
|
$newItems[] = [
|
|
'id' => $id,
|
|
'classname' => $item['classname'] ?? 'unknown_' . $id,
|
|
'name' => $item['name'] ?? '',
|
|
'category' => $item['category'] ?? 'other',
|
|
];
|
|
}
|
|
}
|
|
|
|
// Generate SQL
|
|
$sql = "-- Catalog Update SQL\n";
|
|
$sql .= '-- Generated: ' . date('Y-m-d H:i:s') . "\n";
|
|
$sql .= '-- New items: ' . count($newItems) . "\n\n";
|
|
|
|
foreach ($newItems as $item) {
|
|
$sql .= 'INSERT INTO catalog_items (item_ids, page_id, catalog_name, cost_credits, cost_points, points_type, amount, limited_stack, limited_sells, order_number, have_offer, club_only) ';
|
|
$sql .= 'VALUES (' . $item['id'] . ", 2, '" . $item['classname'] . "', 10, 0, 0, 1, 0, 0, 0, '1', '0');\n";
|
|
}
|
|
|
|
// Save to file
|
|
$sqlPath = '/var/www/atomcms/storage/logs/catalog_update_' . date('Ymd_His') . '.sql';
|
|
file_put_contents($sqlPath, $sql);
|
|
|
|
return [
|
|
'new_items' => count($newItems),
|
|
'sql_file' => $sqlPath,
|
|
'items' => array_slice($newItems, 0, 50),
|
|
];
|
|
}
|
|
|
|
public function findMissingItems(): array
|
|
{
|
|
$furnitureData = $this->getFurnitureData();
|
|
|
|
$catalogItemIds = DB::table('catalog_items')
|
|
->pluck('item_ids')
|
|
->map(fn ($id) => (int) $id)
|
|
->toArray();
|
|
|
|
$validCategories = [
|
|
'chair', 'table', 'shelf', 'bed', 'rug', 'floor', 'lighting',
|
|
'wall_decoration', 'divider', 'teleport', 'gate', 'roller',
|
|
'wired', 'wired_effect', 'wired_condition', 'wired_trigger', 'wired_add_on',
|
|
'music', 'sound_fx', 'vending_machine', 'games', 'fortuna',
|
|
'pets', 'food', 'trophy', 'present', 'credit', 'tent', 'extras',
|
|
'leaderboards', 'other', 'window',
|
|
];
|
|
|
|
$missing = [];
|
|
|
|
foreach ($furnitureData as $item) {
|
|
$id = $item['id'];
|
|
$category = $item['category'] ?? 'unknown';
|
|
$classname = $item['classname'] ?? '';
|
|
|
|
if (in_array($id, $catalogItemIds)) {
|
|
continue;
|
|
}
|
|
|
|
if ($category === 'unknown' || $category === '') {
|
|
continue;
|
|
}
|
|
|
|
if (str_starts_with($classname, 'avatar_effect')) {
|
|
continue;
|
|
}
|
|
|
|
if (! in_array($category, $validCategories)) {
|
|
continue;
|
|
}
|
|
|
|
$missing[] = [
|
|
'id' => $id,
|
|
'classname' => $classname,
|
|
'name' => $item['name'] ?? '',
|
|
'category' => $category,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'total_missing' => count($missing),
|
|
'items' => array_slice($missing, 0, 100),
|
|
];
|
|
}
|
|
|
|
public function addMissingItemsToCatalog(?int $pageId = null): array
|
|
{
|
|
$furnitureData = $this->getFurnitureData();
|
|
|
|
$catalogItemIds = DB::table('catalog_items')
|
|
->pluck('item_ids')
|
|
->map(fn ($id) => (int) $id)
|
|
->toArray();
|
|
|
|
if ($pageId === null) {
|
|
$overigePage = DB::table('catalog_pages')
|
|
->where('caption', 'Overige')
|
|
->where('parent_id', '>', 0)
|
|
->first();
|
|
|
|
if ($overigePage) {
|
|
$pageId = $overigePage->id;
|
|
} else {
|
|
$meubelsPageId = DB::table('catalog_pages')
|
|
->where('caption', 'Meubels')
|
|
->first()?->id ?? 2;
|
|
|
|
$pageId = DB::table('catalog_pages')->insertGetId([
|
|
'parent_id' => $meubelsPageId,
|
|
'caption' => 'Overige',
|
|
'caption_save' => 'Overige',
|
|
'order_num' => 99,
|
|
'visible' => 1,
|
|
'enabled' => '1',
|
|
'club_only' => '0',
|
|
'vip_only' => '0',
|
|
'page_layout' => 'default_3x3',
|
|
'page_headline' => 'Overige',
|
|
'page_teaser' => '',
|
|
'icon_color' => 1,
|
|
'icon_image' => 1,
|
|
'min_rank' => 1,
|
|
]);
|
|
}
|
|
}
|
|
|
|
$order = DB::table('catalog_items')->max('order_number') ?? 0;
|
|
|
|
$validCategories = [
|
|
'chair', 'table', 'shelf', 'bed', 'rug', 'floor', 'lighting',
|
|
'wall_decoration', 'divider', 'teleport', 'gate', 'roller',
|
|
'wired', 'wired_effect', 'wired_condition', 'wired_trigger', 'wired_add_on',
|
|
'music', 'sound_fx', 'vending_machine', 'games', 'fortuna',
|
|
'pets', 'food', 'trophy', 'present', 'credit', 'tent', 'extras',
|
|
'leaderboards', 'other', 'window',
|
|
];
|
|
|
|
$inserts = [];
|
|
foreach ($furnitureData as $item) {
|
|
$id = $item['id'];
|
|
if (in_array($id, $catalogItemIds)) {
|
|
continue;
|
|
}
|
|
|
|
$classname = $item['classname'] ?? '';
|
|
$category = $item['category'] ?? 'unknown';
|
|
|
|
if (str_starts_with($classname, 'avatar_effect')) {
|
|
continue;
|
|
}
|
|
|
|
if ($category === 'unknown' || $category === '') {
|
|
continue;
|
|
}
|
|
|
|
if (! in_array($category, $validCategories)) {
|
|
continue;
|
|
}
|
|
|
|
$inserts[] = [
|
|
'item_ids' => $id,
|
|
'page_id' => $pageId,
|
|
'catalog_name' => $classname,
|
|
'cost_credits' => $this->estimatePrice($category),
|
|
'cost_points' => 0,
|
|
'points_type' => 0,
|
|
'amount' => 1,
|
|
'limited_stack' => 0,
|
|
'limited_sells' => 0,
|
|
'order_number' => ++$order,
|
|
'offer_id' => $item['offerid'] ?? 0,
|
|
'song_id' => 0,
|
|
'extradata' => '',
|
|
'have_offer' => '1',
|
|
'club_only' => '0',
|
|
];
|
|
}
|
|
|
|
$added = 0;
|
|
if ($inserts !== []) {
|
|
foreach (array_chunk($inserts, 500) as $chunk) {
|
|
DB::table('catalog_items')->insert($chunk);
|
|
$added += count($chunk);
|
|
}
|
|
}
|
|
|
|
return [
|
|
'added' => $added,
|
|
'page_id' => $pageId,
|
|
];
|
|
}
|
|
|
|
public function fullSync(string $baseUrl): array
|
|
{
|
|
Log::info('[CatalogService] Starting full sync from: ' . $baseUrl);
|
|
|
|
$results = [
|
|
'furniture' => 0,
|
|
'product' => 0,
|
|
'icons' => 0,
|
|
'catalog_images' => 0,
|
|
'items_base_inserted' => 0,
|
|
'items_base_updated' => 0,
|
|
'catalog_inserted' => 0,
|
|
'catalog_organized' => 0,
|
|
'errors' => [],
|
|
];
|
|
|
|
// Step 1: Download FurnitureData.json
|
|
$furnitureUrl = rtrim($baseUrl, '/') . '/gamedata/config/FurnitureData.json';
|
|
$localFurniturePath = '/var/www/Gamedata/config/FurnitureData.json';
|
|
|
|
$furnitureContent = @file_get_contents($furnitureUrl);
|
|
if ($furnitureContent && $furnitureData = json_decode($furnitureContent, true)) {
|
|
if (file_exists($localFurniturePath)) {
|
|
copy($localFurniturePath, $localFurniturePath . '.backup');
|
|
}
|
|
file_put_contents($localFurniturePath, $furnitureContent);
|
|
$results['furniture'] = count($furnitureData['roomitemtypes']['furnitype'] ?? []);
|
|
} else {
|
|
$results['errors'][] = 'FurnitureData.json niet bereikbaar';
|
|
|
|
return $results;
|
|
}
|
|
|
|
// Step 2: Download ProductData.json
|
|
$productUrl = rtrim($baseUrl, '/') . '/gamedata/config/ProductData.json';
|
|
$productContent = @file_get_contents($productUrl);
|
|
if ($productContent && json_decode($productContent, true)) {
|
|
file_put_contents('/var/www/Gamedata/config/ProductData.json', $productContent);
|
|
$results['product'] = 1;
|
|
}
|
|
|
|
// Step 3: Sync items_base
|
|
$sync = $this->syncItemsBase($furnitureData['roomitemtypes']['furnitype'] ?? []);
|
|
$results['items_base_inserted'] = $sync['inserted'];
|
|
$results['items_base_updated'] = $sync['updated'];
|
|
|
|
// Step 4: Sync catalog_items
|
|
$sync = $this->syncCatalogItems($furnitureData['roomitemtypes']['furnitype'] ?? []);
|
|
$results['catalog_inserted'] = $sync['inserted'];
|
|
|
|
// Step 5: Organize catalog
|
|
$organize = $this->organizeCatalog();
|
|
$results['catalog_organized'] = $organize['items_organized'];
|
|
|
|
// Step 6: Download icons
|
|
$results['icons'] = $this->syncIcons($furnitureData['roomitemtypes']['furnitype'] ?? [], $baseUrl);
|
|
|
|
// Step 7: Download catalogue images
|
|
$results['catalog_images'] = $this->syncCatalogImages($baseUrl);
|
|
|
|
Log::info('[CatalogService] Full sync completed', $results);
|
|
|
|
return $results;
|
|
}
|
|
|
|
private function syncItemsBase(array $furnitureData): array
|
|
{
|
|
$inserted = 0;
|
|
$updated = 0;
|
|
|
|
foreach ($furnitureData as $item) {
|
|
$id = (int) $item['id'];
|
|
$classname = $item['classname'] ?? 'unknown_' . $id;
|
|
|
|
$exists = DB::table('items_base')->where('id', $id)->exists();
|
|
|
|
if ($exists) {
|
|
DB::table('items_base')->where('id', $id)->update([
|
|
'item_name' => $classname,
|
|
'public_name' => $item['name'] ?? $classname,
|
|
'interaction_type' => $item['interactiontype'] ?? 'default',
|
|
]);
|
|
$updated++;
|
|
} else {
|
|
DB::table('items_base')->insert([
|
|
'id' => $id,
|
|
'sprite_id' => $id,
|
|
'item_name' => $classname,
|
|
'public_name' => $item['name'] ?? $classname,
|
|
'type' => 's',
|
|
'width' => 1,
|
|
'length' => 1,
|
|
'stack_height' => 0,
|
|
'allow_stack' => 1,
|
|
'allow_sit' => 0,
|
|
'allow_lay' => 0,
|
|
'allow_walk' => 0,
|
|
'allow_gift' => 1,
|
|
'allow_trade' => 1,
|
|
'allow_recycle' => 0,
|
|
'allow_marketplace_sell' => 1,
|
|
'allow_inventory_stack' => 1,
|
|
'interaction_type' => $item['interactiontype'] ?? 'default',
|
|
'interaction_modes_count' => 1,
|
|
]);
|
|
$inserted++;
|
|
}
|
|
}
|
|
|
|
return ['inserted' => $inserted, 'updated' => $updated];
|
|
}
|
|
|
|
private function syncCatalogItems(array $furnitureData): array
|
|
{
|
|
$inserted = 0;
|
|
$existingIds = DB::table('catalog_items')->pluck('item_ids')->map(fn ($id) => (int) $id)->toArray();
|
|
|
|
$validCategories = [
|
|
'chair', 'table', 'shelf', 'bed', 'rug', 'floor', 'lighting',
|
|
'wall_decoration', 'divider', 'teleport', 'gate', 'roller',
|
|
'wired', 'wired_effect', 'wired_condition', 'wired_trigger', 'wired_add_on',
|
|
'music', 'sound_fx', 'vending_machine', 'games', 'fortuna',
|
|
'pets', 'food', 'trophy', 'present', 'credit', 'tent', 'extras',
|
|
'leaderboards', 'other', 'window',
|
|
];
|
|
|
|
$overigePage = DB::table('catalog_pages')
|
|
->where('caption', 'Overige')
|
|
->where('parent_id', '>', 0)
|
|
->first();
|
|
|
|
$defaultPageId = $overigePage?->id ?? 2;
|
|
$order = DB::table('catalog_items')->max('order_number') ?? 0;
|
|
|
|
$batch = [];
|
|
foreach ($furnitureData as $item) {
|
|
$id = (int) $item['id'];
|
|
if (in_array($id, $existingIds)) {
|
|
continue;
|
|
}
|
|
|
|
$classname = $item['classname'] ?? '';
|
|
$category = $item['category'] ?? 'unknown';
|
|
|
|
if (str_starts_with($classname, 'avatar_effect')) {
|
|
continue;
|
|
}
|
|
|
|
if ($category === 'unknown' || $category === '' || ! in_array($category, $validCategories)) {
|
|
continue;
|
|
}
|
|
|
|
$batch[] = [
|
|
'item_ids' => $id,
|
|
'page_id' => $defaultPageId,
|
|
'catalog_name' => $classname,
|
|
'cost_credits' => $this->estimatePrice($category),
|
|
'cost_points' => 0,
|
|
'points_type' => 0,
|
|
'amount' => 1,
|
|
'limited_stack' => 0,
|
|
'limited_sells' => 0,
|
|
'order_number' => ++$order,
|
|
'offer_id' => $item['offerid'] ?? 0,
|
|
'song_id' => 0,
|
|
'extradata' => '',
|
|
'have_offer' => '1',
|
|
'club_only' => '0',
|
|
];
|
|
}
|
|
|
|
if ($batch !== []) {
|
|
foreach (array_chunk($batch, 500) as $chunk) {
|
|
DB::table('catalog_items')->insert($chunk);
|
|
$inserted += count($chunk);
|
|
}
|
|
}
|
|
|
|
return ['inserted' => $inserted];
|
|
}
|
|
|
|
private function syncIcons(array $furnitureData, string $baseUrl): int
|
|
{
|
|
$downloaded = 0;
|
|
$iconsPath = '/var/www/Gamedata/icons';
|
|
$iconsUrl = rtrim($baseUrl, '/') . '/gamedata/icons';
|
|
|
|
$iconNames = [];
|
|
foreach ($furnitureData as $item) {
|
|
$classname = $item['classname'] ?? '';
|
|
if ($classname && ! str_starts_with((string) $classname, 'avatar_effect')) {
|
|
$iconNames[] = $classname . '_icon.png';
|
|
}
|
|
}
|
|
|
|
$iconNames = array_unique($iconNames);
|
|
|
|
foreach (array_slice($iconNames, 0, 2000) as $iconName) {
|
|
$localFile = $iconsPath . '/' . $iconName;
|
|
|
|
if (! file_exists($localFile)) {
|
|
$iconUrl = $iconsUrl . '/' . $iconName;
|
|
$content = @file_get_contents($iconUrl);
|
|
|
|
if ($content && strlen($content) > 100) {
|
|
file_put_contents($localFile, $content);
|
|
$downloaded++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $downloaded;
|
|
}
|
|
|
|
private function syncCatalogImages(string $baseUrl): int
|
|
{
|
|
$downloaded = 0;
|
|
$catalogPath = '/var/www/Gamedata/catalogue';
|
|
$catalogUrl = rtrim($baseUrl, '/') . '/gamedata/catalogue';
|
|
|
|
$commonImages = [
|
|
'header.png', 'footer.png', 'info.png', 'past.png',
|
|
'club_header.png', 'club_past.png', 'vip_header.png',
|
|
];
|
|
|
|
foreach ($commonImages as $image) {
|
|
$localFile = $catalogPath . '/' . $image;
|
|
if (! file_exists($localFile)) {
|
|
$imageUrl = $catalogUrl . '/' . $image;
|
|
$content = @file_get_contents($imageUrl);
|
|
|
|
if ($content && strlen($content) > 100) {
|
|
file_put_contents($localFile, $content);
|
|
$downloaded++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $downloaded;
|
|
}
|
|
|
|
public function getStats(): array
|
|
{
|
|
return [
|
|
'total_catalog_items' => DB::table('catalog_items')->count(),
|
|
'total_pages' => DB::table('catalog_pages')->count(),
|
|
'total_items_base' => DB::table('items_base')->count(),
|
|
'items_with_pages' => DB::table('catalog_items')->where('page_id', '>', 0)->count(),
|
|
'items_without_pages' => DB::table('catalog_items')->where(function ($q) {
|
|
$q->whereNull('page_id')->orWhere('page_id', 0);
|
|
})->count(),
|
|
];
|
|
}
|
|
|
|
public function syncCatalogClothing(): array
|
|
{
|
|
Log::info('[CatalogService] Starting catalog_clothing sync');
|
|
|
|
$figureMapPath = '/var/www/Gamedata/config/FigureMap.json';
|
|
|
|
if (! file_exists($figureMapPath)) {
|
|
throw new \Exception('FigureMap.json niet gevonden');
|
|
}
|
|
|
|
$figureMap = json_decode(file_get_contents($figureMapPath), true);
|
|
|
|
// Clear table first
|
|
DB::table('catalog_clothing')->truncate();
|
|
|
|
$inserted = 0;
|
|
|
|
$batch = [];
|
|
|
|
foreach ($figureMap['libraries'] ?? [] as $library) {
|
|
$libId = $library['id'];
|
|
$partIds = [];
|
|
|
|
foreach ($library['parts'] as $part) {
|
|
$partIds[] = (string) $part['id'];
|
|
}
|
|
|
|
// Get unique part IDs
|
|
$uniquePartIds = array_unique($partIds);
|
|
$setIds = implode(',', $uniquePartIds);
|
|
|
|
// Skip if setid is too long for database (varchar 75)
|
|
if (strlen($setIds) > 75) {
|
|
$truncated = [];
|
|
$length = 0;
|
|
foreach ($uniquePartIds as $id) {
|
|
if ($length + strlen($id) + 1 > 75) {
|
|
break;
|
|
}
|
|
$truncated[] = $id;
|
|
$length += strlen($id) + 1;
|
|
}
|
|
$setIds = implode(',', $truncated);
|
|
}
|
|
|
|
// Convert library name to clothing name format
|
|
$clothingName = 'clothing_' . $this->libraryToClothingName($libId);
|
|
|
|
// Clean and validate set IDs
|
|
$cleanSetIds = trim($setIds, ',');
|
|
if (strlen($cleanSetIds) < 1) {
|
|
continue;
|
|
}
|
|
|
|
// Filter out IDs that are too large (>= 2^31, overflows 32-bit integer)
|
|
$validIds = [];
|
|
foreach (explode(',', $cleanSetIds) as $id) {
|
|
$id = (int) trim($id);
|
|
if ($id > 0 && $id < 2147483647) {
|
|
$validIds[] = $id;
|
|
}
|
|
}
|
|
if ($validIds === []) {
|
|
continue;
|
|
}
|
|
$cleanSetIds = implode(',', $validIds);
|
|
|
|
$batch[] = [
|
|
'name' => $clothingName,
|
|
'setid' => $cleanSetIds,
|
|
];
|
|
}
|
|
|
|
// Insert in batches
|
|
foreach (array_chunk($batch, 500) as $chunk) {
|
|
DB::table('catalog_clothing')->insert($chunk);
|
|
$inserted += count($chunk);
|
|
}
|
|
|
|
Log::info('[CatalogService] Catalog clothing sync complete', [
|
|
'inserted' => $inserted,
|
|
]);
|
|
|
|
return [
|
|
'inserted' => $inserted,
|
|
'total' => DB::table('catalog_clothing')->count(),
|
|
];
|
|
}
|
|
|
|
private function libraryToClothingName(string $libId): string
|
|
{
|
|
// Convert library name like "shirt_F_yogatop" to "yogatop"
|
|
// Remove gender suffix (_F, _M, _U)
|
|
$name = preg_replace('/_[FMU]$/', '', $libId);
|
|
|
|
// Remove prefix (shirt_, jacket_, hat_, etc.)
|
|
$name = preg_replace('/^(shirt|jacket|hat|trousers|acc_eye|acc_face|acc_chest|acc_waist|shoes|hair|eye|head|face|chest|waist)_?/', '', (string) $name);
|
|
|
|
return strtolower((string) $name);
|
|
}
|
|
|
|
public function syncCatalogClothingFromFurnitureData(): array
|
|
{
|
|
Log::info('[CatalogService] Starting catalog_clothing sync from FurnitureData');
|
|
|
|
$furnitureData = $this->getFurnitureData();
|
|
|
|
// Get existing clothing
|
|
$existingClothing = DB::table('catalog_clothing')->pluck('setid', 'name')->toArray();
|
|
|
|
$inserted = 0;
|
|
$updated = 0;
|
|
|
|
foreach ($furnitureData as $item) {
|
|
$classname = $item['classname'] ?? '';
|
|
|
|
// Check if this is a clothing item (usually starts with 'clothing_')
|
|
if (! str_starts_with($classname, 'clothing_')) {
|
|
continue;
|
|
}
|
|
|
|
// Get set ID from extradata
|
|
$setid = $item['extradata'] ?? $item['params'] ?? '';
|
|
|
|
if (empty($setid)) {
|
|
continue;
|
|
}
|
|
|
|
// Truncate if too long (column is varchar 75)
|
|
$setid = substr((string) $setid, 0, 75);
|
|
|
|
if (isset($existingClothing[$classname])) {
|
|
if ($existingClothing[$classname] !== $setid) {
|
|
DB::table('catalog_clothing')
|
|
->where('name', $classname)
|
|
->update(['setid' => $setid]);
|
|
$updated++;
|
|
}
|
|
} else {
|
|
DB::table('catalog_clothing')->insert([
|
|
'name' => $classname,
|
|
'setid' => $setid,
|
|
]);
|
|
$inserted++;
|
|
}
|
|
}
|
|
|
|
return [
|
|
'inserted' => $inserted,
|
|
'updated' => $updated,
|
|
'total' => DB::table('catalog_clothing')->count(),
|
|
];
|
|
}
|
|
}
|