|
@@ -10,6 +10,7 @@
|
|
|
<li><a href="{{ url_for('admin.user_search') }}">User Search</a></li>
|
|
<li><a href="{{ url_for('admin.user_search') }}">User Search</a></li>
|
|
|
<li><a>Players</a></li>
|
|
<li><a>Players</a></li>
|
|
|
<li><a>Bans</a></li>
|
|
<li><a>Bans</a></li>
|
|
|
|
|
+ <li><a href="{{ url_for('admin.event_settings') }}">Event Settings</a></li>
|
|
|
</ul>
|
|
</ul>
|
|
|
</aside>
|
|
</aside>
|
|
|
</div>
|
|
</div>
|
|
@@ -21,12 +22,14 @@
|
|
|
|
|
|
|
|
<div class="field is-grouped">
|
|
<div class="field is-grouped">
|
|
|
<div class="control">
|
|
<div class="control">
|
|
|
|
|
+ <div class="select">
|
|
|
|
|
+ <select id="package-filter">
|
|
|
|
|
+ <option value="ALL">All Packages</option>
|
|
|
|
|
+ </select>
|
|
|
|
|
+ </div>
|
|
|
<div class="select">
|
|
<div class="select">
|
|
|
<select id="event-filter">
|
|
<select id="event-filter">
|
|
|
<option value="ALL">All Events</option>
|
|
<option value="ALL">All Events</option>
|
|
|
- <option value="CHAT">Chat</option>
|
|
|
|
|
- <option value="JOIN">Join/Quit</option>
|
|
|
|
|
- <option value="DEATH">Death</option>
|
|
|
|
|
</select>
|
|
</select>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -43,72 +46,122 @@
|
|
|
<script>
|
|
<script>
|
|
|
const socket = io();
|
|
const socket = io();
|
|
|
const feed = document.getElementById('event-feed');
|
|
const feed = document.getElementById('event-feed');
|
|
|
- const filter = document.getElementById('event-filter');
|
|
|
|
|
|
|
+ const packageFilter = document.getElementById('package-filter');
|
|
|
|
|
+ const eventFilter = document.getElementById('event-filter'); // Now dynamic
|
|
|
|
|
+
|
|
|
|
|
+ let registeredEvents = []; // List of full class names
|
|
|
|
|
|
|
|
socket.on('connect', () => {
|
|
socket.on('connect', () => {
|
|
|
console.log('Connected to WebSocket');
|
|
console.log('Connected to WebSocket');
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- socket.on('game_event', (data) => {
|
|
|
|
|
|
|
+ socket.on('event_metadata', (data) => {
|
|
|
|
|
+ console.log("Received metadata", data);
|
|
|
|
|
+ registeredEvents = data.events;
|
|
|
|
|
+ populateFilters();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ socket.on('universal_event', (data) => {
|
|
|
|
|
+ // data = {type: 'PlayerJoinEvent', package: 'org.bukkit...', event_data: {...}, ...}
|
|
|
|
|
+
|
|
|
const div = document.createElement('div');
|
|
const div = document.createElement('div');
|
|
|
div.className = 'notification is-small is-dark';
|
|
div.className = 'notification is-small is-dark';
|
|
|
div.style.marginBottom = '0.5rem';
|
|
div.style.marginBottom = '0.5rem';
|
|
|
div.style.padding = '0.5rem';
|
|
div.style.padding = '0.5rem';
|
|
|
- div.dataset.type = data.type; // Store type for filtering
|
|
|
|
|
|
|
+ div.dataset.package = data.package;
|
|
|
|
|
+ div.dataset.type = data.type;
|
|
|
|
|
|
|
|
- // Check if we should show this event based on current filter
|
|
|
|
|
- if (!shouldShowEvent(data.type, filter.value)) {
|
|
|
|
|
|
|
+ // Filtering
|
|
|
|
|
+ if (!shouldShowEvent(data.package, data.type)) {
|
|
|
div.style.display = 'none';
|
|
div.style.display = 'none';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- let content = '';
|
|
|
|
|
const time = new Date(data.timestamp).toLocaleTimeString();
|
|
const time = new Date(data.timestamp).toLocaleTimeString();
|
|
|
|
|
+ let content = `[${time}] <strong>${data.type}</strong> `;
|
|
|
|
|
|
|
|
- switch (data.type) {
|
|
|
|
|
- case 'CHAT':
|
|
|
|
|
- content = `[${time}] <strong>${data.player}</strong>: ${data.content}`;
|
|
|
|
|
- break;
|
|
|
|
|
- case 'JOIN':
|
|
|
|
|
- content = `[${time}] <span class="has-text-success">+</span> <strong>${data.player}</strong> joined the game.`;
|
|
|
|
|
- break;
|
|
|
|
|
- case 'QUIT':
|
|
|
|
|
- content = `[${time}] <span class="has-text-danger">-</span> <strong>${data.player}</strong> left the game.`;
|
|
|
|
|
- break;
|
|
|
|
|
- case 'DEATH':
|
|
|
|
|
- content = `[${time}] <span class="has-text-danger">☠</span> ${data.content}`;
|
|
|
|
|
- break;
|
|
|
|
|
- default:
|
|
|
|
|
- content = `[${time}] [${data.type}] ${data.content}`;
|
|
|
|
|
|
|
+ if (data.player) {
|
|
|
|
|
+ content += `(${data.player}) `;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Format event_data
|
|
|
|
|
+ if (data.event_data) {
|
|
|
|
|
+ content += `<br/><span style="font-size: 0.8em; color: #ccc;">${JSON.stringify(data.event_data).substring(0, 200)}</span>`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
div.innerHTML = content;
|
|
div.innerHTML = content;
|
|
|
feed.appendChild(div);
|
|
feed.appendChild(div);
|
|
|
|
|
|
|
|
- // Only scroll if visible or if we want to force scroll (usually good to scroll if new event arrived)
|
|
|
|
|
if (div.style.display !== 'none') {
|
|
if (div.style.display !== 'none') {
|
|
|
feed.scrollTop = feed.scrollHeight;
|
|
feed.scrollTop = feed.scrollHeight;
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- filter.addEventListener('change', () => {
|
|
|
|
|
- const selectedFilter = filter.value;
|
|
|
|
|
- const events = feed.children;
|
|
|
|
|
-
|
|
|
|
|
- for (let event of events) {
|
|
|
|
|
- const type = event.dataset.type;
|
|
|
|
|
- if (shouldShowEvent(type, selectedFilter)) {
|
|
|
|
|
- event.style.display = '';
|
|
|
|
|
- } else {
|
|
|
|
|
- event.style.display = 'none';
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Listen for filter changes
|
|
|
|
|
+ packageFilter.addEventListener('change', () => {
|
|
|
|
|
+ populateEventFilter(); // Update event dropdown based on package
|
|
|
|
|
+ applyFilters();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- function shouldShowEvent(eventType, filterValue) {
|
|
|
|
|
- if (filterValue === 'ALL') return true;
|
|
|
|
|
- if (filterValue === eventType) return true;
|
|
|
|
|
- if (filterValue === 'JOIN' && (eventType === 'JOIN' || eventType === 'QUIT')) return true;
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ eventFilter.addEventListener('change', applyFilters);
|
|
|
|
|
+
|
|
|
|
|
+ function populateFilters() {
|
|
|
|
|
+ // Get unique packages
|
|
|
|
|
+ const packages = new Set(registeredEvents.map(e => e.substring(0, e.lastIndexOf('.'))));
|
|
|
|
|
+
|
|
|
|
|
+ packageFilter.innerHTML = '<option value="ALL">All Packages</option>';
|
|
|
|
|
+ [...packages].sort().forEach(pkg => {
|
|
|
|
|
+ const opt = document.createElement('option');
|
|
|
|
|
+ opt.value = pkg;
|
|
|
|
|
+ opt.textContent = pkg.replace('org.bukkit.event.', '...'); // Shorten common prefix
|
|
|
|
|
+ packageFilter.appendChild(opt);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ populateEventFilter();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function populateEventFilter() {
|
|
|
|
|
+ const selectedPackage = packageFilter.value;
|
|
|
|
|
+ const currentSelection = eventFilter.value;
|
|
|
|
|
+
|
|
|
|
|
+ eventFilter.innerHTML = '<option value="ALL">All Events</option>';
|
|
|
|
|
+
|
|
|
|
|
+ registeredEvents
|
|
|
|
|
+ .filter(e => selectedPackage === 'ALL' || e.startsWith(selectedPackage))
|
|
|
|
|
+ .map(e => e.substring(e.lastIndexOf('.') + 1)) // Get simple name
|
|
|
|
|
+ .sort()
|
|
|
|
|
+ .forEach(simpleName => {
|
|
|
|
|
+ const opt = document.createElement('option');
|
|
|
|
|
+ opt.value = simpleName;
|
|
|
|
|
+ opt.textContent = simpleName;
|
|
|
|
|
+ eventFilter.appendChild(opt);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // restore selection if valid
|
|
|
|
|
+ // simplistic restoration, might reset if package changed effectively
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function applyFilters() {
|
|
|
|
|
+ const pFilter = packageFilter.value;
|
|
|
|
|
+ const eFilter = eventFilter.value;
|
|
|
|
|
+
|
|
|
|
|
+ for (let div of feed.children) {
|
|
|
|
|
+ const p = div.dataset.package;
|
|
|
|
|
+ const t = div.dataset.type;
|
|
|
|
|
+
|
|
|
|
|
+ let show = true;
|
|
|
|
|
+ if (pFilter !== 'ALL' && p !== pFilter) show = false;
|
|
|
|
|
+ if (eFilter !== 'ALL' && t !== eFilter) show = false;
|
|
|
|
|
+
|
|
|
|
|
+ div.style.display = show ? '' : 'none';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function shouldShowEvent(pkg, type) {
|
|
|
|
|
+ const pFilter = packageFilter.value;
|
|
|
|
|
+ const eFilter = eventFilter.value;
|
|
|
|
|
+ if (pFilter !== 'ALL' && pkg !== pFilter) return false;
|
|
|
|
|
+ if (eFilter !== 'ALL' && type !== eFilter) return false;
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function clearFeed() {
|
|
function clearFeed() {
|