console.log('๐ Avatar Outfit Property Inspector ๋ก๋๋จ');
// ํ๋ฉด์ ๋ก๊ทธ๋ฅผ ํ์ํ๋ ํจ์
function logToScreen(msg, color = "#00ff00") {
let logDiv = document.getElementById('logArea');
if (!logDiv) {
return; // ๋ก๊ทธ ์์ญ์ด ์์ง ์์ผ๋ฉด ๋ฌด์
}
const line = document.createElement('div');
line.style.color = color;
line.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`;
logDiv.appendChild(line);
logDiv.scrollTop = logDiv.scrollHeight;
// ๋ก๊ทธ๊ฐ ๋๋ฌด ๋ง์์ง๋ฉด ์ค๋๋ ๊ฒ ์ ๊ฑฐ (์ต๋ 100์ค)
if (logDiv.children.length > 100) {
logDiv.removeChild(logDiv.firstChild);
}
}
// ๊ธฐ์กด console.log/console.error๋ฅผ ํ๋ฉด์๋ ์ถ๋ ฅ
const origLog = console.log;
console.log = function(...args) {
origLog.apply(console, args);
const msg = args.map(a => (typeof a === 'object' ? JSON.stringify(a, null, 2) : a)).join(' ');
logToScreen(msg, '#00ff00');
};
const origError = console.error;
console.error = function(...args) {
origError.apply(console, args);
const msg = args.map(a => (typeof a === 'object' ? JSON.stringify(a, null, 2) : a)).join(' ');
logToScreen(msg, '#ff5555');
};
const origWarn = console.warn;
console.warn = function(...args) {
origWarn.apply(console, args);
const msg = args.map(a => (typeof a === 'object' ? JSON.stringify(a, null, 2) : a)).join(' ');
logToScreen(msg, '#ffaa00');
};
// ์ ์ญ ๋ณ์
let websocket = null;
let uuid = null; // Property Inspector UUID
let actionContext = null; // ๋ฒํผ๋ณ ๊ณ ์ ์ปจํ
์คํธ
let actionInfo = null;
let avatarList = [];
let isUnityConnected = false;
let settings = {};
// Stream Deck ์ฐ๊ฒฐ
function connectElgatoStreamDeckSocket(inPort, inPropertyInspectorUUID, inRegisterEvent, inInfo, inActionInfo) {
console.log('๐ Avatar Outfit Property Inspector ์ฐ๊ฒฐ ์ค...');
console.log('๐ก ํฌํธ:', inPort, 'UUID:', inPropertyInspectorUUID);
uuid = inPropertyInspectorUUID;
// Parse action info - ๋ฒํผ๋ณ ๊ณ ์ ์ปจํ
์คํธ์ ์ค์ ์ถ์ถ
try {
if (inActionInfo) {
console.log('๐ Raw actionInfo:', inActionInfo);
actionInfo = JSON.parse(inActionInfo);
actionContext = actionInfo.context; // ๋ฒํผ๋ณ ๊ณ ์ ์ปจํ
์คํธ
settings = actionInfo.payload?.settings || {};
// actionType์ด ์์ผ๋ฉด ๊ฐ์ ๋ก ์ค์
if (!settings.actionType) {
settings.actionType = 'avatar_outfit';
console.log('๐ง actionType์ด ์์ด์ ๊ฐ์ ๋ก ์ค์ :', settings.actionType);
}
console.log('โ๏ธ Property Inspector UUID:', uuid);
console.log('โ๏ธ ๋ฒํผ ์ปจํ
์คํธ:', actionContext);
console.log('โ๏ธ ์ด๊ธฐ ์ค์ :', settings);
console.log('โ๏ธ actionInfo ์ ์ฒด:', actionInfo);
} else {
console.error('โ actionInfo๊ฐ ์์ต๋๋ค!');
}
} catch (error) {
console.error('โ ActionInfo ํ์ฑ ์ค๋ฅ:', error);
console.log('๐ ํ์ฑ ์๋ํ ๋ฌธ์์ด:', inActionInfo);
}
websocket = new WebSocket('ws://localhost:' + inPort);
websocket.onopen = function() {
console.log('โ
Property Inspector ์ฐ๊ฒฐ๋จ');
// StreamDeck์ ๋ฑ๋ก (์นด๋ฉ๋ผ์ ๋์ผํ ํจํด)
websocket.send(JSON.stringify({
event: inRegisterEvent,
uuid: inPropertyInspectorUUID
}));
// Unity ์ํ ์์ฒญ (์นด๋ฉ๋ผ์ ๋์ผํ ํจํด)
setTimeout(() => {
console.log('๐ Plugin์ ๋๋ฒ๊ทธ ๋ฉ์์ง ์ ์ก ์์');
// actionType์ด ์ค์ ๋์์ผ๋ฉด ์ฆ์ Plugin์ ์ ์ฅ
if (settings.actionType === 'avatar_outfit') {
const actionTypeMessage = {
action: 'com.mirabox.streamingle.avatar_outfit',
event: 'setSettings',
context: uuid,
payload: settings
};
websocket.send(JSON.stringify(actionTypeMessage));
console.log('๐ง ์ด๊ธฐ actionType ์ค์ ์ Plugin์ ์ ์ฅ:', settings);
}
// ๋จผ์ Plugin์ด ์ด ๋ฒํผ์ ์ธ์ํ๋์ง ํ์ธ
sendToPlugin('debug_test', {
message: 'Avatar outfit controller test',
context: actionContext,
action: 'com.mirabox.streamingle.avatar_outfit'
});
// Unity ์ํ ์์ฒญ
sendToPlugin('get_unity_status');
}, 500);
// ์ด๊ธฐ ์ค์ ๋ก๋
loadInitialSettings();
};
websocket.onmessage = function(evt) {
try {
const jsonObj = JSON.parse(evt.data);
handleMessage(jsonObj);
} catch (error) {
console.error('โ ๋ฉ์์ง ํ์ฑ ์ค๋ฅ:', error);
}
};
websocket.onclose = function() {
console.log('โ Property Inspector ์ฐ๊ฒฐ ๋์ด์ง');
websocket = null;
// ์ฐ๊ฒฐ ์ํ๋ฅผ ๋น์ฐ๊ฒฐ๋ก ํ์
handleUnityConnected(false);
};
websocket.onerror = function(error) {
console.error('โ Property Inspector ์ฐ๊ฒฐ ์ค๋ฅ:', error);
websocket = null;
// ์ฐ๊ฒฐ ์ํ๋ฅผ ๋น์ฐ๊ฒฐ๋ก ํ์
handleUnityConnected(false);
};
}
// ๋ฉ์์ง ์ฒ๋ฆฌ
function handleMessage(jsonObj) {
console.log('๐จ ๋ฉ์์ง ์์ :', jsonObj.event);
console.log('๐จ ์ ์ฒด ๋ฉ์์ง:', JSON.stringify(jsonObj, null, 2));
// sendToPropertyInspector ์ด๋ฒคํธ ์ฒ๋ฆฌ (์นด๋ฉ๋ผ ๋ฐฉ์๊ณผ ๋์ผ)
if (jsonObj.event === 'sendToPropertyInspector' && jsonObj.payload && jsonObj.payload.event) {
const innerEvent = jsonObj.payload.event;
console.log('๐จ ๋ด๋ถ ์ด๋ฒคํธ:', innerEvent, 'payload:', jsonObj.payload);
switch (innerEvent) {
case 'unity_connected':
console.log('โ
Unity ์ฐ๊ฒฐ ์ํ ์
๋ฐ์ดํธ');
handleUnityConnected(jsonObj.payload.connected);
break;
case 'avatar_outfit_list':
console.log('๐ ์๋ฐํ ์์ ๋ชฉ๋ก ์์ ');
handleAvatarOutfitList(jsonObj.payload);
break;
case 'avatar_outfit_changed':
console.log('๐ญ ์๋ฐํ ์์ ๋ณ๊ฒฝ ์๋ฆผ');
handleAvatarOutfitChanged(jsonObj.payload);
break;
case 'camera_list':
console.log('โ ๏ธ ์นด๋ฉ๋ผ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ง๋ง ์๋ฐํ ์ปจํธ๋กค๋ฌ์์๋ ๋ฌด์ํจ');
break;
case 'item_list':
console.log('โ ๏ธ ์์ดํ
๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ง๋ง ์๋ฐํ ์ปจํธ๋กค๋ฌ์์๋ ๋ฌด์ํจ');
break;
case 'event_list':
console.log('โ ๏ธ ์ด๋ฒคํธ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ง๋ง ์๋ฐํ ์ปจํธ๋กค๋ฌ์์๋ ๋ฌด์ํจ');
break;
case 'debug_response':
console.log('โ
Plugin์ผ๋ก๋ถํฐ ๋๋ฒ๊ทธ ์๋ต ๋ฐ์!');
console.log('๐จ ์๋ต ๋ฐ์ดํฐ:', jsonObj.payload);
break;
default:
console.log('โ ์ ์ ์๋ ๋ด๋ถ ์ด๋ฒคํธ:', innerEvent);
break;
}
return;
}
switch(jsonObj.event) {
case 'sendToPropertyInspector':
if (jsonObj.payload) {
handlePluginMessage(jsonObj.payload);
}
break;
case 'didReceiveSettings':
if (jsonObj.payload && jsonObj.payload.settings) {
updateUIFromSettings(jsonObj.payload.settings);
}
break;
// ๊ธฐ์กด ๋ฐฉ์๋ ์ ์ง (ํธํ์ฑ)
case 'unity_connected':
console.log('โ
Unity ์ฐ๊ฒฐ ์ํ ์
๋ฐ์ดํธ (๊ธฐ์กด ๋ฐฉ์)');
handleUnityConnected(jsonObj.payload?.connected);
break;
case 'avatar_outfit_list':
console.log('๐ ์๋ฐํ ์์ ๋ชฉ๋ก ์์ (๊ธฐ์กด ๋ฐฉ์)');
handleAvatarOutfitList(jsonObj.payload);
break;
case 'avatar_outfit_changed':
console.log('๐ญ ์๋ฐํ ์์ ๋ณ๊ฒฝ ์๋ฆผ (๊ธฐ์กด ๋ฐฉ์)');
handleAvatarOutfitChanged(jsonObj.payload);
break;
}
}
// ํ๋ฌ๊ทธ์ธ ๋ฉ์์ง ์ฒ๋ฆฌ
function handlePluginMessage(payload) {
console.log('๐จ ํ๋ฌ๊ทธ์ธ ๋ฉ์์ง:', payload.event, payload);
switch(payload.event) {
case 'unity_connected':
console.log('๐ Unity ์ฐ๊ฒฐ ์ด๋ฒคํธ ์์ :', payload);
handleUnityConnected(payload.connected);
break;
case 'avatar_outfit_list':
console.log('๐ ์๋ฐํ ์์ ๋ชฉ๋ก ์ด๋ฒคํธ ์์ :', payload);
handleAvatarOutfitList(payload);
break;
case 'avatar_outfit_changed':
console.log('๐ญ ์๋ฐํ ์์ ๋ณ๊ฒฝ ์ด๋ฒคํธ ์์ :', payload);
handleAvatarOutfitChanged(payload);
break;
default:
console.log('โ ์ ์ ์๋ ์ด๋ฒคํธ:', payload.event);
break;
}
}
// Unity ์ฐ๊ฒฐ ์ํ ์ฒ๋ฆฌ
function handleUnityConnected(connected) {
console.log('๐ Unity ์ฐ๊ฒฐ ์ํ:', connected);
isUnityConnected = connected;
const statusIndicator = document.getElementById('connection-status');
const statusDot = document.getElementById('status-dot');
const statusText = document.getElementById('status-text');
const connectionDetail = document.getElementById('connection-detail');
const avatarSelect = document.getElementById('avatar-select');
const refreshButton = document.getElementById('refresh-button');
if (connected) {
statusIndicator.classList.add('connected');
statusDot.classList.add('connected');
statusText.textContent = 'Unity ์๋ฒ ์ฐ๊ฒฐ๋จ';
connectionDetail.textContent = '์๋ฐํ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ์ค...';
avatarSelect.disabled = false;
refreshButton.disabled = false;
avatarSelect.innerHTML = '';
// ์ฐ๊ฒฐ ์ฆ์ ์๋ฐํ ๋ชฉ๋ก ์์ฒญ
console.log('๐ Unity ์ฐ๊ฒฐ๋จ - ์๋ฐํ ๋ชฉ๋ก ์์ฒญ');
setTimeout(() => {
refreshAvatarList();
}, 500);
} else {
statusIndicator.classList.remove('connected');
statusDot.classList.remove('connected');
statusText.textContent = 'Unity ์๋ฒ ์ฐ๊ฒฐ ์๋จ';
connectionDetail.textContent = 'Unity๋ฅผ ์คํํ๊ณ AvatarOutfitController๊ฐ ์๋์ง ํ์ธํ์ธ์';
avatarSelect.disabled = true;
refreshButton.disabled = true;
avatarSelect.innerHTML = '';
updateOutfitSelect(-1);
}
}
// ์๋ฐํ ์์ ๋ชฉ๋ก ์ฒ๋ฆฌ
function handleAvatarOutfitList(payload) {
console.log('๐ ์๋ฐํ ์์ ๋ชฉ๋ก ์์ :', payload);
console.log('๐ payload.avatars:', payload.avatars);
console.log('๐ Array.isArray(payload.avatars):', Array.isArray(payload.avatars));
if (payload.avatars && Array.isArray(payload.avatars)) {
avatarList = payload.avatars;
console.log('๐ ์๋ฐํ ๋ชฉ๋ก ์ ์ฅ๋จ:', avatarList.length, '๊ฐ');
console.log('๐ ์๋ฐํ ๋ชฉ๋ก ์์ธ:', avatarList);
updateAvatarSelect();
// ํ์ฌ ์ค์ ๋ ์๋ฐํ ํ์ธ
const avatarSelect = document.getElementById('avatar-select');
if (avatarSelect.value !== '') {
const selectedIndex = parseInt(avatarSelect.value);
if (!isNaN(selectedIndex)) {
updateOutfitSelect(selectedIndex);
}
}
} else {
console.log('โ ์๋ฐํ ๋ฐ์ดํฐ๊ฐ ์ฌ๋ฐ๋ฅด์ง ์์:', payload.avatars);
avatarList = [];
updateAvatarSelect();
}
updateCurrentStatus(payload.currentIndex);
}
// ์๋ฐํ ์์ ๋ณ๊ฒฝ ์ฒ๋ฆฌ
function handleAvatarOutfitChanged(payload) {
console.log('๐ญ ์๋ฐํ ์์ ๋ณ๊ฒฝ:', payload);
handleAvatarOutfitList(payload); // ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌ
}
// ์๋ฐํ ์ ํ ์
๋ฐ์ดํธ
function updateAvatarSelect() {
const avatarSelect = document.getElementById('avatar-select');
const connectionDetail = document.getElementById('connection-detail');
avatarSelect.innerHTML = '';
if (avatarList.length === 0) {
avatarSelect.innerHTML = '';
connectionDetail.textContent = 'Unity์์ AvatarOutfitController์ ์๋ฐํ๋ฅผ ์ถ๊ฐํ์ธ์';
return;
}
avatarSelect.innerHTML = '';
avatarList.forEach((avatar, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = `${avatar.name} (${avatar.outfits ? avatar.outfits.length : 0}๊ฐ ์์)`;
avatarSelect.appendChild(option);
});
connectionDetail.textContent = `${avatarList.length}๊ฐ์ ์๋ฐํ๋ฅผ ๋ฐ๊ฒฌํ์ต๋๋ค`;
}
// ์์ ์ ํ ์
๋ฐ์ดํธ
function updateOutfitSelect(avatarIndex) {
const outfitSelect = document.getElementById('outfit-select');
outfitSelect.innerHTML = '';
if (avatarIndex < 0 || avatarIndex >= avatarList.length) {
outfitSelect.innerHTML = '';
outfitSelect.disabled = true;
return;
}
const avatar = avatarList[avatarIndex];
if (!avatar.outfits || avatar.outfits.length === 0) {
outfitSelect.innerHTML = '';
outfitSelect.disabled = true;
return;
}
// ๊ธฐ๋ณธ ์ ํ ์ต์
์ถ๊ฐ
outfitSelect.innerHTML = '';
// ์์ ๋ชฉ๋ก ์ถ๊ฐ
avatar.outfits.forEach((outfit, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = outfit.name;
outfitSelect.appendChild(option);
});
outfitSelect.disabled = false;
// ๊ธฐ๋ณธ๊ฐ ์ค์ (์ฒซ ๋ฒ์งธ ์์)
if (avatar.outfits.length > 0) {
outfitSelect.value = 0;
}
}
// ํ์ฌ ์ํ ์
๋ฐ์ดํธ
function updateCurrentStatus(currentAvatarIndex) {
const currentAvatar = document.getElementById('current-avatar');
const currentOutfit = document.getElementById('current-outfit');
if (currentAvatarIndex >= 0 && currentAvatarIndex < avatarList.length) {
const avatar = avatarList[currentAvatarIndex];
currentAvatar.textContent = `ํ์ฌ: ${avatar.name}`;
currentOutfit.textContent = `ํ์ฌ ์์: ${avatar.current_outfit_name || '์ ๋ณด ์์'}`;
} else {
currentAvatar.textContent = 'ํ์ฌ: ์ ํ๋์ง ์์';
currentOutfit.textContent = 'ํ์ฌ ์์: ์ ๋ณด ์์';
}
}
// Send message to plugin
function sendToPlugin(command, data = {}) {
console.log('๐ sendToPlugin ํธ์ถ:', command, data);
console.log('๐ WebSocket ์ํ:', websocket ? 'available' : 'null');
console.log('๐ actionContext ์ํ:', actionContext);
if (!websocket) {
console.error('โ WebSocket not available');
return;
}
if (!uuid) {
console.error('โ UUID not available');
return;
}
try {
const message = {
action: 'com.mirabox.streamingle.avatar_outfit', // manifest.json์ Action UUID
event: 'sendToPlugin',
context: uuid, // Property Inspector UUID (Stream Deck ํ์ค)
payload: {
command,
buttonContext: actionContext, // ๋ฒํผ์ ์ค์ context ์ ๋ณด๋ ํจ๊ป ์ ์ก
...data
}
};
websocket.send(JSON.stringify(message));
console.log('๐ค Message sent to plugin:', command, data, 'context:', uuid);
} catch (error) {
console.error('โ Failed to send message to plugin:', error);
}
}
// Unity ์ํ ์์ฒญ
function requestUnityStatus() {
sendToPlugin('get_unity_status');
}
// ์๋ฐํ ๋ชฉ๋ก ์๋ก๊ณ ์นจ
function refreshAvatarList() {
sendToPlugin('refresh_avatar_outfit_list');
}
// ์ค์ ์ ์ฅ
function saveSettings() {
if (!uuid) {
console.error('โ No uuid available for saving settings');
return;
}
if (!websocket) {
console.log('โ ๏ธ WebSocket์ด ์ฐ๊ฒฐ๋์ง ์์, ์ค์ ์ ์ฅ ๊ฑด๋๋');
return;
}
const avatarSelect = document.getElementById('avatar-select');
const outfitSelect = document.getElementById('outfit-select');
const newSettings = {
avatarIndex: parseInt(avatarSelect.value || '0'),
outfitIndex: parseInt(outfitSelect.value || '0'),
actionType: 'avatar_outfit' // ๐ฅ ์ค์: actionType ์ถ๊ฐ!
};
// ์ ์ญ ์ค์ ๋ ์
๋ฐ์ดํธ
settings = { ...settings, ...newSettings };
const message = {
action: 'com.mirabox.streamingle.avatar_outfit',
event: 'setSettings',
context: uuid, // Property Inspector UUID ์ฌ์ฉ (Stream Deck ํ์ค)
payload: newSettings
};
try {
websocket.send(JSON.stringify(message));
console.log('๐ค ์ค์ ์ ์ฅ๋จ - Context:', uuid, 'Settings:', newSettings);
// ์ค์ ์ด ์ ์ฅ๋๋ฉด Plugin์๊ฒ ์๋ฐํ ์์ ๋ณ๊ฒฝ ์์ฒญ๋ ๋ณด๋
sendToPlugin('set_avatar_outfit', {
avatarIndex: newSettings.avatarIndex,
outfitIndex: newSettings.outfitIndex
});
// ๋ฒํผ ์ ๋ชฉ ์
๋ฐ์ดํธ ์์ฒญ
sendToPlugin('update_title', {
avatarIndex: newSettings.avatarIndex,
outfitIndex: newSettings.outfitIndex
});
} catch (error) {
console.error('โ ์ค์ ์ ์ฅ ์คํจ:', error);
websocket = null;
handleUnityConnected(false);
}
}
// ์ด๊ธฐ ์ค์ ๋ก๋
function loadInitialSettings() {
console.log('๐ง ์ด๊ธฐ ์ค์ ๋ก๋ ์์');
console.log('๐ง ํ์ฌ settings:', settings);
// ์ด๊ธฐํ๊ฐ ์๋ฃ๋ ํ UI ์
๋ฐ์ดํธ
setTimeout(() => {
updateUIFromSettings(settings);
}, 100);
}
// ์ค์ ์์ UI ์
๋ฐ์ดํธ
function updateUIFromSettings(settingsToApply) {
console.log('๐จ UI ์
๋ฐ์ดํธ ์์:', settingsToApply);
const avatarSelect = document.getElementById('avatar-select');
const outfitSelect = document.getElementById('outfit-select');
if (settingsToApply.avatarIndex !== undefined && avatarSelect) {
console.log('๐ค ์๋ฐํ ์ธ๋ฑ์ค ์ ์ฉ:', settingsToApply.avatarIndex);
avatarSelect.value = settingsToApply.avatarIndex;
updateOutfitSelect(settingsToApply.avatarIndex);
}
if (settingsToApply.outfitIndex !== undefined && outfitSelect) {
console.log('๐ ์์ ์ธ๋ฑ์ค ์ ์ฉ:', settingsToApply.outfitIndex);
outfitSelect.value = settingsToApply.outfitIndex;
}
console.log('โ
UI ์
๋ฐ์ดํธ ์๋ฃ');
}
// ์ด๋ฒคํธ ๋ฆฌ์ค๋
document.addEventListener('DOMContentLoaded', function() {
console.log('๐ Avatar Outfit Property Inspector DOM ์ค๋น๋จ');
// ์๋ฐํ ์ ํ ๋ณ๊ฒฝ
const avatarSelect = document.getElementById('avatar-select');
if (avatarSelect) {
avatarSelect.addEventListener('change', function() {
const selectedIndex = parseInt(this.value);
if (!isNaN(selectedIndex)) {
updateOutfitSelect(selectedIndex);
} else {
updateOutfitSelect(-1);
}
saveSettings();
});
}
// ์์ ์ ํ ๋ณ๊ฒฝ
const outfitSelect = document.getElementById('outfit-select');
if (outfitSelect) {
outfitSelect.addEventListener('change', saveSettings);
}
// ์๋ก๊ณ ์นจ ๋ฒํผ
const refreshButton = document.getElementById('refresh-button');
if (refreshButton) {
refreshButton.addEventListener('click', function() {
console.log('๐ ์๋ก๊ณ ์นจ ๋ฒํผ ํด๋ฆญ๋จ');
// Unity ์ํ๋ฅผ ๋ค์ ํ์ธํ๊ณ ์๋ฐํ ๋ชฉ๋ก๋ ์๋ก๊ณ ์นจ
requestUnityStatus();
refreshAvatarList();
// ๊ฐ์ ๋ก ์ฐ๊ฒฐ ์ํ๋ฅผ true๋ก ์ค์ (๋ค๋ฅธ ์ปจํธ๋กค๋ฌ๊ฐ ์ฐ๊ฒฐ๋์ด ์๋ค๋ฉด Unity๋ ์๋ ์ค)
setTimeout(() => {
console.log('๐ง ์๋ก๊ณ ์นจ ํ ๊ฐ์ ๋ก Unity ์ฐ๊ฒฐ ์ํ๋ฅผ true๋ก ์ค์ ');
handleUnityConnected(true);
}, 1000);
});
}
});
console.log('โ
Avatar Outfit Property Inspector ์คํฌ๋ฆฝํธ ๋ก๋ ์๋ฃ');