Changes for v3.4

added headless search mode (#237)
added user defined font selection
added translatable biomes names
added shortcut to copy active seed with CTRL+C and remapped copy-seed-list (#238)
changed warning messages to work in headless mode
changed zoom shortcuts to accept Qt defaults (#217)
removed dejavu fonts
This commit is contained in:
Cubitect 2023-06-25 21:53:05 +02:00
parent e67bcbb588
commit 026a9c6e03
56 changed files with 1867 additions and 1103 deletions

@ -1 +1 @@
Subproject commit f2c96306c77e216120beb4c65b90624c526a3ba5
Subproject commit f81c2e4fdea0fa96d89c963be5948e0a1d1025d9

View File

@ -33,17 +33,23 @@ static_gnu: {
LIBS += -static -static-libgcc -static-libstdc++
}
gcc {
greaterThan(QMAKE_GCC_MAJOR_VERSION, 9): QMAKE_CXXFLAGS += -Wno-deprecated-copy
}
CONFIG(debug, debug|release): {
CUTARGET = debug
QMAKE_CFLAGS += -fsanitize=undefined
LIBS += -lubsan -ldl
!win32 {
QMAKE_CFLAGS += -fsanitize=undefined
LIBS += -lubsan -ldl
}
} else {
CUTARGET = release
}
# compile cubiomes
CUPATH = $$PWD/cubiomes
QMAKE_PRE_LINK += $(MAKE) -C $$CUPATH -f $$CUPATH/makefile CFLAGS=\"$$QMAKE_CFLAGS\" $$CUTARGET
QMAKE_PRE_LINK += $(MAKE) -C $$CUPATH -f $$CUPATH/makefile CC=\"$$QMAKE_CC\" CFLAGS=\"$$QMAKE_CFLAGS\" $$CUTARGET
QMAKE_CLEAN += $$CUPATH/*.o $$CUPATH/libcubiomes.a
LIBS += -lm $$CUPATH/libcubiomes.a
@ -96,6 +102,8 @@ SOURCES += \
src/formgen48.cpp \
src/formsearchcontrol.cpp \
src/gotodialog.cpp \
src/headless.cpp \
src/message.cpp \
src/presetdialog.cpp \
src/protobasedialog.cpp \
src/layerdialog.cpp \
@ -159,6 +167,8 @@ HEADERS += \
src/formgen48.h \
src/formsearchcontrol.h \
src/gotodialog.h \
src/headless.h \
src/message.h \
src/presetdialog.h \
src/protobasedialog.h \
src/layerdialog.h \

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -73,8 +73,8 @@
<file>icons/check_include_d.png</file>
<file>icons/check_unchecked.png</file>
<file>icons/height.png</file>
<file>icons/trail.png</file>
<file>icons/trail_d.png</file>
<file>icons/trails.png</file>
<file>icons/trails_d.png</file>
<file>icons/missing.png</file>
<file>icons/well.png</file>
<file>icons/well_d.png</file>

View File

Before

Width:  |  Height:  |  Size: 371 B

After

Width:  |  Height:  |  Size: 371 B

View File

Before

Width:  |  Height:  |  Size: 363 B

After

Width:  |  Height:  |  Size: 363 B

View File

@ -209,8 +209,5 @@
<file>dark/window_undock_pressed.png</file>
<file>dark/window_undock_pressed@2x.png</file>
<file>dark.qss</file>
<file>fonts/DejaVuSans.ttf</file>
<file>fonts/DejaVuSans-Bold.ttf</file>
<file>fonts/DejaVuSansMono.ttf</file>
</qresource>
</RCC>

View File

@ -5,8 +5,8 @@
#include <QString>
#define VERS_MAJOR 3
#define VERS_MINOR 3
#define VERS_PATCH 0 // negative patch number designates a development version
#define VERS_MINOR 4
#define VERS_PATCH -1 // negative patch number designates a development version
// returns +1 if newer, -1 if older and 0 if equal
inline int cmpVers(int major, int minor, int patch)

View File

@ -2,14 +2,6 @@
<ui version="4.0">
<class>AboutDialog</class>
<widget class="QDialog" name="AboutDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>615</width>
<height>427</height>
</rect>
</property>
<property name="windowTitle">
<string>About</string>
</property>
@ -27,7 +19,7 @@
<item row="0" column="2" rowspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:16pt; font-weight:600;&quot;&gt;Cubiomes-Viewer _VERSION_&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Built: _DATE_&lt;/p&gt;&lt;p&gt;URL: &lt;a href=&quot;https://github.com/Cubitect/cubiomes-viewer&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;https://github.com/Cubitect/cubiomes-viewer&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;License: &lt;a href=&quot;https://www.gnu.org/licenses/gpl-3.0.en.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;GPLv3&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;span style=&quot; font-size:12pt; font-weight:600;&quot;&gt;Components and Legal Information&lt;/span&gt;&lt;/p&gt;&lt;p&gt;— Minecraft biome and structure generation from &lt;a href=&quot;https://github.com/Cubitect/cubiomes/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;cubiomes&lt;/span&gt;&lt;/a&gt;, licensed under &lt;a href=&quot;https://mit-license.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;MIT&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;— Cross platform GUI toolkit: Qt _QT_MAJOR_._QT_MINOR_, available under &lt;a href=&quot;https://www.qt.io/licensing/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;(L)GPLv3&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;— Dark Qt theme derived from &lt;a href=&quot;https://github.com/ColinDuquesnoy/QDarkStyleSheet&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;QDarkStyleSheet&lt;/span&gt;&lt;/a&gt;, licensed under MIT.&lt;/p&gt;&lt;p&gt;— A copy of the &lt;a href=&quot;https://dejavu-fonts.github.io/License.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;DejaVu Fonts&lt;/span&gt;&lt;/a&gt; is included in this build.&lt;/p&gt;&lt;p&gt;— Biome colors and icons are inspired by &lt;a href=&quot;https://github.com/toolbox4minecraft/amidst&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;Amidst&lt;/span&gt;&lt;/a&gt;, licensed under GPLv3.&lt;/p&gt;&lt;p&gt;— &lt;a href=&quot;https://www.lua.org/license.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;Lua _LUA_VERS_&lt;/span&gt;&lt;/a&gt; is distributed under the terms of the MIT license.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;NOT APPROVED BY OR ASSOCIATED WITH MOJANG.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:16pt; font-weight:600;&quot;&gt;Cubiomes-Viewer _VERSION_&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Built: _DATE_&lt;/p&gt;&lt;p&gt;URL: &lt;a href=&quot;https://github.com/Cubitect/cubiomes-viewer&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;https://github.com/Cubitect/cubiomes-viewer&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;License: &lt;a href=&quot;https://www.gnu.org/licenses/gpl-3.0.en.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;GPLv3&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;span style=&quot; font-size:12pt; font-weight:600;&quot;&gt;Components and Legal Information&lt;/span&gt;&lt;/p&gt;&lt;p&gt;— Minecraft biome and structure generation from &lt;a href=&quot;https://github.com/Cubitect/cubiomes/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;cubiomes&lt;/span&gt;&lt;/a&gt;, licensed under &lt;a href=&quot;https://mit-license.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;MIT&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;— Cross platform GUI toolkit: Qt _QT_MAJOR_._QT_MINOR_, available under &lt;a href=&quot;https://www.qt.io/licensing/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;(L)GPLv3&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;— Dark Qt theme derived from &lt;a href=&quot;https://github.com/ColinDuquesnoy/QDarkStyleSheet&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;QDarkStyleSheet&lt;/span&gt;&lt;/a&gt;, licensed under MIT.&lt;/p&gt;&lt;p&gt;— Biome colors and icons are inspired by &lt;a href=&quot;https://github.com/toolbox4minecraft/amidst&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;Amidst&lt;/span&gt;&lt;/a&gt;, licensed under GPLv3.&lt;/p&gt;&lt;p&gt;— &lt;a href=&quot;https://www.lua.org/license.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;Lua _LUA_VERS_&lt;/span&gt;&lt;/a&gt; is distributed under the terms of the MIT license.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;NOT APPROVED BY OR ASSOCIATED WITH MOJANG.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>

View File

@ -69,8 +69,8 @@ BiomeColorDialog::BiomeColorDialog(QWidget *parent, QString initrc, int mc, int
for (int i = 0; i < 256; i++)
{
const char *bname = biome2str(mc, i);
if (!bname)
QString bname = getBiomeDisplay(mc, i);
if (bname.isEmpty() || bname == "?")
continue;
QColor col;

View File

@ -4,6 +4,7 @@
#include "mainwindow.h"
#include "scripts.h"
#include "layerdialog.h"
#include "message.h"
#include <QCheckBox>
#include <QIntValidator>
@ -11,7 +12,6 @@
#include <QPixmap>
#include <QPainter>
#include <QPainterPath>
#include <QMessageBox>
#include <QScrollBar>
#include <QSpacerItem>
#include <QTextStream>
@ -38,10 +38,10 @@ static QString getTip(int mc, int layer, uint32_t flags, int id)
QString tip = ConditionDialog::tr("Generates any of:");
for (int j = 0; j < 64; j++)
if (mL & (1ULL << j))
tip += QString("\n") + biome2str(mc, j);
tip += QString("\n") + getBiomeDisplay(mc, j);
for (int j = 0; j < 64; j++)
if (mM & (1ULL << j))
tip += QString("\n") + biome2str(mc, 128+j);
tip += QString("\n") + getBiomeDisplay(mc, 128+j);
return tip;
}
@ -66,8 +66,6 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
QString mcs = tr("MC %1", "Minecraft version").arg(p_mcs ? p_mcs : "?");
ui->labelMC->setText(mcs);
ui->lineSummary->setFont(*gp_font_mono);
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
ui->textEditLua->setTabStopWidth(QFontMetrics(ui->textEditLua->font()).width(" ") * 4);
#else
@ -92,7 +90,7 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
if (c.save == initcond->relative)
initindex = ui->comboBoxRelative->count();
}
QString condstr = c.summary().simplified();
QString condstr = c.summary(nullptr).simplified();
ui->comboBoxRelative->addItem(condstr, c.save);
}
if (initindex < 0)
@ -130,10 +128,10 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
for (int i = 0; i < 256; i++)
{
const char *str = biome2str(mc, i);
if (!str)
QString bname = getBiomeDisplay(mc, i);
if (bname.isEmpty())
continue;
QCheckBox *cb = new QCheckBox(str);
QCheckBox *cb = new QCheckBox(bname);
ui->gridLayoutBiomes->addWidget(cb, i % 128, i / 128);
cb->setTristate(true);
biomecboxes[i] = cb;
@ -245,7 +243,7 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
const int *lim = getBiomeParaLimits(mc, id);
if (!lim)
continue;
NoiseBiomeIndicator *cb = new NoiseBiomeIndicator(biome2str(mc, id), this);
NoiseBiomeIndicator *cb = new NoiseBiomeIndicator(getBiomeDisplay(mc, id), this);
QString tip = "<pre>";
for (int j = 0; j < NP_MAX; j++)
{
@ -327,7 +325,7 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
on_comboBoxRelative_activated(initindex);
ui->textEditLua->document()->setModified(false);
ui->comboMatchBiome->insertItem(0, biome2str(mc, cond.biomeId), QVariant::fromValue(cond.biomeId));
ui->comboMatchBiome->insertItem(0, getBiomeDisplay(mc, cond.biomeId), QVariant::fromValue(cond.biomeId));
ui->comboMatchBiome->setCurrentIndex(0);
ui->comboClimatePara->setCurrentIndex(ui->comboClimatePara->findData(QVariant::fromValue(cond.para)));
@ -610,7 +608,7 @@ void ConditionDialog::updateMode()
ui->lineEditX2->setToolTip(uptip);
ui->lineEditZ2->setToolTip(uptip);
ui->buttonOk->setEnabled(filterindex != F_SELECT);
textDescription->setText(ft.description);
textDescription->setText(QApplication::translate("Filter", ft.description));
}
void ConditionDialog::updateBiomeSelection()
@ -668,12 +666,12 @@ void ConditionDialog::updateBiomeSelection()
for (int j = 0; j < 64; j++)
{
if (mL & (1ULL << j))
tip += QString("\n") + biome2str(mc, j);
tip += QString("\n") + getBiomeDisplay(mc, j);
}
for (int j = 0; j < 64; j++)
{
if (mM & (1ULL << j))
tip += QString("\n") + biome2str(mc, j+128);
tip += QString("\n") + getBiomeDisplay(mc, j+128);
}
cb->setToolTip(tip);
}
@ -741,7 +739,7 @@ void ConditionDialog::updateBiomeSelection()
for (int id: available)
{
QString s = biome2str(mc, id);
QString s = getBiomeDisplay(mc, id);
ui->comboMatchBiome->addItem(getBiomeIcon(id), s, QVariant::fromValue(id));
allowed_matches.append(s);
}
@ -754,7 +752,7 @@ void ConditionDialog::updateBiomeSelection()
}
else
{
QString s = QString("%1 %2").arg(WARNING_CHAR).arg(biome2str(mc, curid.toInt()));
QString s = QString("%1 %2").arg(WARNING_CHAR).arg(getBiomeDisplay(mc, curid.toInt()));
ui->comboMatchBiome->insertItem(0, getBiomeIcon(curid.toInt(), true), s, curid);
ui->comboMatchBiome->setCurrentIndex(0);
allowed_matches.append(s);
@ -778,7 +776,7 @@ int ConditionDialog::warnIfBad(Condition cond)
if (ui->checkStartPieces->isEnabled())
{
QString text = tr("No allowed start pieces specified. Condition can never be true.");
QMessageBox::warning(this, tr("Missing Start Piece"), text, QMessageBox::Ok);
warn(this, tr("Missing Start Piece"), text);
return QMessageBox::Cancel;
}
}
@ -792,8 +790,7 @@ int ConditionDialog::warnIfBad(Condition cond)
"The condition contains a climate range which is unbounded "
"with the full range required, which can never be satisfied."
);
QMessageBox::warning(this, tr("Bad Climate Range"), text,
QMessageBox::Ok);
warn(this, tr("Bad Climate Range"), text);
return QMessageBox::Cancel;
}
}
@ -808,7 +805,7 @@ int ConditionDialog::warnIfBad(Condition cond)
"The biome locator checks for %1 instances of size %2 each, "
"which cannot be satisfied by an area of size %3%4%5 = %6.")
.arg(cond.count).arg(cond.biomeSize).arg(w).arg(QChar(0xD7)).arg(h).arg(w * h);
QMessageBox::warning(this, tr("Area Insufficient"), text, QMessageBox::Ok);
warn(this, tr("Area Insufficient"), text);
return QMessageBox::Cancel;
}
}
@ -823,7 +820,7 @@ int ConditionDialog::warnIfBad(Condition cond)
(1ULL << (deep_dark-128));
if ((m & underground) && cond.y > 246)
{
return QMessageBox::warning(this, tr("Bad Surface Height"),
return warn(this, tr("Bad Surface Height"),
tr("Cave biomes do not generate above Y = 246. "
"You should consider lowering the sampling height."
"\n\n"
@ -1234,9 +1231,9 @@ void ConditionDialog::on_lineBiomeSize_textChanged(const QString &)
double area = ui->lineBiomeSize->text().toInt();
QString s;
if (filterindex == F_BIOME_CENTER_256)
s = QString::asprintf(tr("(~%g sq. chunks)").toStdString().c_str(), area * 256);
s = tr("(~%1 sq. chunks)").arg(area * 256);
else
s = QString::asprintf(tr("(%g sq. chunks)").toStdString().c_str(), area / 16.0);
s = tr("(%1 sq. chunks)").arg(area / 16.0);
ui->labelBiomeSize->setText(s);
}

View File

@ -204,7 +204,7 @@
<string/>
</property>
<property name="maxLength">
<number>26</number>
<number>25</number>
</property>
<property name="placeholderText">
<string/>
@ -685,8 +685,8 @@ QPushButton:hover {
<rect>
<x>0</x>
<y>0</y>
<width>634</width>
<height>301</height>
<width>98</width>
<height>28</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
@ -1069,8 +1069,8 @@ QPushButton:hover {
<rect>
<x>0</x>
<y>0</y>
<width>634</width>
<height>254</height>
<width>98</width>
<height>28</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
@ -1174,7 +1174,7 @@ QPushButton:hover {
<widget class="QComboBox" name="comboOctaves">
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<family>Monospace</family>
</font>
</property>
</widget>
@ -1316,7 +1316,7 @@ QPushButton:hover {
<widget class="ScriptEditor" name="textEditLua">
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<family>Monospace</family>
</font>
</property>
</widget>
@ -1344,7 +1344,7 @@ QPushButton:hover {
<widget class="QPlainTextEdit" name="textEditLuaOut">
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<family>Monospace</family>
</font>
</property>
<property name="readOnly">
@ -1356,7 +1356,7 @@ QPushButton:hover {
<widget class="QLabel" name="labelLuaCall">
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<family>Monospace</family>
</font>
</property>
<property name="text">
@ -1453,8 +1453,8 @@ QPushButton:hover {
<rect>
<x>0</x>
<y>0</y>
<width>634</width>
<height>336</height>
<width>98</width>
<height>28</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3_1">
@ -1510,8 +1510,8 @@ QPushButton:hover {
<rect>
<x>0</x>
<y>0</y>
<width>634</width>
<height>336</height>
<width>98</width>
<height>28</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3_2">
@ -1615,8 +1615,8 @@ QPushButton:hover {
<rect>
<x>0</x>
<y>0</y>
<width>634</width>
<height>336</height>
<width>98</width>
<height>28</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3_3">

View File

@ -1,8 +1,10 @@
#include "config.h"
#include "cutil.h"
#include "seedtables.h"
#include <QThread>
#include <QCoreApplication>
#include <QFontDatabase>
void ExtGenConfig::reset()
@ -120,8 +122,41 @@ bool LayerOpt::isClimate(int mc) const
return mode >= LOPT_NOISE_T_4 && mode <= LOPT_NOISE_W_4;
}
QString mapopt2display(int opt)
{
switch (opt)
{
case D_GRID: return QApplication::translate("Map", "Grid");
case D_SLIME: return QApplication::translate("Map", "Slime Chunks");
case D_DESERT: return QApplication::translate("Map", "Desert Pyramid");
case D_JUNGLE: return QApplication::translate("Map", "Jungle Temple");
case D_HUT: return QApplication::translate("Map", "Swamp Hut");
case D_IGLOO: return QApplication::translate("Map", "Igloo");
case D_VILLAGE: return QApplication::translate("Map", "Village");
case D_MANSION: return QApplication::translate("Map", "Woodland Mansion");
case D_MONUMENT: return QApplication::translate("Map", "Ocean Monument");
case D_RUINS: return QApplication::translate("Map", "Ocean Ruins");
case D_SHIPWRECK: return QApplication::translate("Map", "Shipwreck");
case D_TREASURE: return QApplication::translate("Map", "Buried Treasure");
case D_MINESHAFT: return QApplication::translate("Map", "Mineshaft");
case D_WELL: return QApplication::translate("Map", "Desert Well");
case D_GEODE: return QApplication::translate("Map", "Geode");
case D_OUTPOST: return QApplication::translate("Map", "Pillager Outpost");
case D_ANCIENTCITY: return QApplication::translate("Map", "Ancient City");
case D_TRAILS: return QApplication::translate("Map", "Trail Ruins");
case D_PORTAL: return QApplication::translate("Map", "Ruined Portal");
case D_PORTALN: return QApplication::translate("Map", "Ruined Portal (Nether)");
case D_SPAWN: return QApplication::translate("Map", "Spawn");
case D_STRONGHOLD: return QApplication::translate("Map", "Stronghold");
case D_FORTESS: return QApplication::translate("Map", "Nether Fortress");
case D_BASTION: return QApplication::translate("Map", "Bastion Remnant");
case D_ENDCITY: return QApplication::translate("Map", "End City");
case D_GATEWAY: return QApplication::translate("Map", "End Gateway");
default: return "";
}
}
const char *mapopt2str(int opt)
const char *mapopt2str(int opt) // to resource string
{
switch (opt)
{
@ -142,7 +177,7 @@ const char *mapopt2str(int opt)
case D_GEODE: return "geode";
case D_OUTPOST: return "outpost";
case D_ANCIENTCITY: return "ancient_city";
case D_TRAIL: return "trails";
case D_TRAILS: return "trails";
case D_PORTAL: return "portal";
case D_PORTALN: return "portaln";
case D_SPAWN: return "spawn";
@ -155,7 +190,7 @@ const char *mapopt2str(int opt)
}
}
int str2mapopt(const char *s)
int str2mapopt(const char *s) // from resource string
{
if (!strcmp(s, "grid")) return D_GRID;
if (!strcmp(s, "slime")) return D_SLIME;
@ -174,7 +209,7 @@ int str2mapopt(const char *s)
if (!strcmp(s, "geode")) return D_GEODE;
if (!strcmp(s, "outpost")) return D_OUTPOST;
if (!strcmp(s, "ancient_city")) return D_ANCIENTCITY;
if (!strcmp(s, "trail")) return D_TRAIL;
if (!strcmp(s, "trails")) return D_TRAILS;
if (!strcmp(s, "portal")) return D_PORTAL;
if (!strcmp(s, "portaln")) return D_PORTALN;
if (!strcmp(s, "spawn")) return D_SPAWN;
@ -205,7 +240,7 @@ int mapopt2stype(int opt)
case D_GEODE: return Geode;
case D_OUTPOST: return Outpost;
case D_ANCIENTCITY: return Ancient_City;
case D_TRAIL: return Trail_Ruin;
case D_TRAILS: return Trail_Ruin;
case D_PORTAL: return Ruined_Portal;
case D_PORTALN: return Ruined_Portal_N;
case D_FORTESS: return Fortress;
@ -287,6 +322,14 @@ void Config::reset()
biomeColorPath = "";
separator = ";";
quote = "";
fontNorm = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
fontMono = QFontDatabase::systemFont(QFontDatabase::FixedFont);
//fontNorm.setFamily(QString::fromUtf8("DejaVu Sans"));
//fontMono.setFamily(QString::fromUtf8("DejaVu Sans Mono"));
fontMono.setStyleHint(QFont::Monospace);
fontNorm.setStyleHint(QFont::AnyStyle);
fontMono.setPointSize(10);
fontNorm.setPointSize(10);
}
void Config::load(QSettings& settings)
@ -306,6 +349,8 @@ void Config::load(QSettings& settings)
biomeColorPath = settings.value("config/biomeColorPath", biomeColorPath).toString();
separator = settings.value("config/separator", separator).toString();
quote = settings.value("config/quote", quote).toString();
fontNorm = settings.value("config/fontNorm", fontNorm).value<QFont>();
fontMono = settings.value("config/fontMono", fontMono).value<QFont>();
}
void Config::save(QSettings& settings)
@ -325,6 +370,8 @@ void Config::save(QSettings& settings)
settings.setValue("config/biomeColorPath", biomeColorPath);
settings.setValue("config/separator", separator);
settings.setValue("config/quote", quote);
settings.setValue("config/fontNorm", fontNorm);
settings.setValue("config/fontMono", fontMono);
}
void Gen48Config::reset()
@ -376,6 +423,50 @@ void Gen48Config::write(QTextStream& stream)
}
}
uint64_t Gen48Config::estimateSeedCnt(uint64_t slist48len)
{
uint64_t cnt = 0;
if (mode == GEN48_QH)
{
switch (qual)
{ // simply hardcoded number of seeds in each category
case IDEAL_SALTED: // falltrough
case IDEAL: cnt = 74474; break;
case CLASSIC: cnt = 107959; break;
case NORMAL: cnt = 293716; break;
case BARELY: cnt = 755745; break;
default: cnt = 0;
}
}
else if (mode == GEN48_QM)
{
cnt = 0;
for (int i = 0, n = sizeof(g_qm_90) / sizeof(int64_t); i < n; i++)
if (qmonumentQual(g_qm_90[i]) >= qmarea)
cnt++;
}
else if (mode == GEN48_LIST)
{
cnt = slist48len;
}
else
{
cnt = MASK48 + 1;
}
if (mode != GEN48_NONE)
{
uint64_t w = x2 - x1 + 1;
uint64_t h = z2 - z1 + 1;
uint64_t n = w*h * cnt; // div sizeof(uint64_t)
if (cnt > 0 && n < PRECOMPUTE48_BUFSIZ * sizeof(uint64_t) && n / cnt == w*h)
cnt = n;
else
cnt = MASK48 + 1;
}
return cnt;
}
void SearchConfig::reset()
{
searchtype = SEARCH_INC;

View File

@ -12,6 +12,9 @@
#define APP_STRING "cubiomes-viewer"
#define PRECOMPUTE48_BUFSIZ ((int64_t)1 << 30)
struct ExtGenConfig
{
bool experimentalVers;
@ -113,7 +116,7 @@ enum {
D_GEODE,
D_OUTPOST,
D_ANCIENTCITY,
D_TRAIL,
D_TRAILS,
D_PORTAL,
D_PORTALN,
D_FORTESS,
@ -126,6 +129,7 @@ enum {
D_STRUCT_NUM
};
QString mapopt2display(int opt);
const char *mapopt2str(int opt);
int str2mapopt(const char *s);
int mapopt2stype(int opt);
@ -176,6 +180,8 @@ struct Config
QString biomeColorPath;
QString separator;
QString quote;
QFont fontNorm;
QFont fontMono;
Config() { reset(); }
@ -205,6 +211,8 @@ struct Gen48Config
void reset();
bool read(const QString& line);
void write(QTextStream& stream);
uint64_t estimateSeedCnt(uint64_t slist48len);
};
// search type options from combobox

View File

@ -29,8 +29,6 @@ ConfigDialog::ConfigDialog(QWidget *parent, Config *config)
}
#endif
ui->buttonBiomeColor->setFont(*gp_font_mono);
ui->lineMatching->setValidator(new QIntValidator(1, 99999999, ui->lineMatching));
ui->spinThreads->setRange(1, QThread::idealThreadCount());
@ -75,6 +73,10 @@ void ConfigDialog::initConfig(Config *config)
ui->lineSep->setText(config->separator);
int idx = config->quote == "\'" ? 1 : config->quote== "\"" ? 2 : 0;
ui->comboQuote->setCurrentIndex(idx);
ui->fontComboNorm->setCurrentFont(config->fontNorm);
ui->fontComboMono->setCurrentFont(config->fontMono);
ui->spinFontSizeNorm->setValue(config->fontNorm.pointSize());
ui->spinFontSizeMono->setValue(config->fontMono.pointSize());
setBiomeColorPath(config->biomeColorPath);
}
@ -97,6 +99,13 @@ Config ConfigDialog::getConfig()
int idx = ui->comboQuote->currentIndex();
conf.quote = idx == 1 ? "\'" : idx == 2 ? "\"" : "";
conf.fontNorm = ui->fontComboNorm->currentFont();
conf.fontMono = ui->fontComboMono->currentFont();
conf.fontNorm.setPointSize(ui->spinFontSizeNorm->value());
conf.fontMono.setPointSize(ui->spinFontSizeMono->value());
conf.fontNorm.setStyleHint(QFont::AnyStyle);
conf.fontMono.setStyleHint(QFont::Monospace);
if (!conf.maxMatching) conf.maxMatching = 65536;
return conf;

View File

@ -145,28 +145,111 @@
<string>Interface</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="4">
<widget class="QPushButton" name="buttonClear">
<property name="maximumSize">
<size>
<width>24</width>
<height>16777215</height>
</size>
<item row="7" column="0" colspan="2">
<widget class="QLabel" name="label_6">
<property name="toolTip">
<string>Use a fixed grid in blocks instead of outlining the generated map tiles
Leave blank for the default behaviour</string>
</property>
<property name="icon">
<iconset resource="../rc/icons.qrc">
<normaloff>:/icons/clear.png</normaloff>:/icons/clear.png</iconset>
<property name="text">
<string>Custom grid spacing:</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Map cache size:</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Language:</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Font default:</string>
</property>
</widget>
</item>
<item row="11" column="0" colspan="5">
<widget class="QCheckBox" name="checkBBoxes">
<property name="text">
<string>Outline known bounding boxes</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Biome color customization:</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Maximum structure zoom:</string>
</property>
</widget>
</item>
<item row="7" column="2" colspan="3">
<widget class="QLineEdit" name="lineGridSpacing"/>
</item>
<item row="5" column="2" colspan="2">
<widget class="QPushButton" name="buttonBiomeColor">
<property name="text">
<string>[none]</string>
</property>
</widget>
</item>
<item row="0" column="2" colspan="3">
<widget class="QComboBox" name="comboLang"/>
</item>
<item row="4" column="2" colspan="3">
<widget class="QPushButton" name="buttonBiomeColorEditor">
<property name="text">
<string>Edit biome colors...</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Custom grid multiplier:</string>
</property>
</widget>
</item>
<item row="6" column="2" colspan="3">
<widget class="QPushButton" name="buttonStructVisEdit">
<property name="text">
<string>Edit map visibility limits...</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>GUI style:</string>
</property>
</widget>
</item>
<item row="12" column="0" colspan="5">
<widget class="QCheckBox" name="checkSmooth">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Simulate inertia for the map view&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Smooth map motion</string>
</property>
</widget>
</item>
<item row="9" column="2" colspan="3">
<widget class="QSpinBox" name="spinCacheSize">
<property name="suffix">
<string> MB</string>
@ -185,50 +268,7 @@
</property>
</widget>
</item>
<item row="0" column="2" colspan="3">
<widget class="QComboBox" name="comboStyle">
<property name="iconSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<item>
<property name="text">
<string>System</string>
</property>
</item>
<item>
<property name="text">
<string>Dark</string>
</property>
</item>
</widget>
</item>
<item row="5" column="2" colspan="3">
<widget class="QLineEdit" name="lineGridSpacing"/>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Maximum structure zoom:</string>
</property>
</widget>
</item>
<item row="1" column="2" colspan="3">
<widget class="QComboBox" name="comboLang"/>
</item>
<item row="8" column="2" colspan="3">
<widget class="QSpinBox" name="spinThreads"/>
</item>
<item row="3" column="2" colspan="2">
<widget class="QPushButton" name="buttonBiomeColor">
<property name="text">
<string>[none]</string>
</property>
</widget>
</item>
<item row="6" column="2" colspan="3">
<widget class="QComboBox" name="comboGridMult">
<property name="enabled">
<bool>false</bool>
@ -260,53 +300,7 @@
</item>
</widget>
</item>
<item row="2" column="2" colspan="3">
<widget class="QPushButton" name="buttonBiomeColorEditor">
<property name="text">
<string>Edit biome colors...</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="5">
<widget class="QCheckBox" name="checkBBoxes">
<property name="text">
<string>Outline known bounding boxes</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Custom grid multiplier:</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Language:</string>
</property>
</widget>
</item>
<item row="4" column="2" colspan="3">
<widget class="QPushButton" name="buttonStructVisEdit">
<property name="text">
<string>Edit map visibility limits...</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QLabel" name="label_6">
<property name="toolTip">
<string>Use a fixed grid in blocks instead of outlining the generated map tiles
Leave blank for the default behaviour</string>
</property>
<property name="text">
<string>Custom grid spacing:</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
@ -342,37 +336,96 @@ QPushButton:hover {
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>GUI style:</string>
</property>
</widget>
</item>
<item row="10" column="0" colspan="5">
<widget class="QCheckBox" name="checkSmooth">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Simulate inertia for the map view&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Smooth map motion</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Map cache size:</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<item row="10" column="0" colspan="2">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Threads for map:</string>
</property>
</widget>
</item>
<item row="5" column="4">
<widget class="QPushButton" name="buttonClear">
<property name="maximumSize">
<size>
<width>24</width>
<height>16777215</height>
</size>
</property>
<property name="icon">
<iconset resource="../rc/icons.qrc">
<normaloff>:/icons/clear.png</normaloff>:/icons/clear.png</iconset>
</property>
</widget>
</item>
<item row="1" column="2" colspan="3">
<widget class="QComboBox" name="comboStyle">
<property name="iconSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<item>
<property name="text">
<string>System</string>
</property>
</item>
<item>
<property name="text">
<string>Dark</string>
</property>
</item>
</widget>
</item>
<item row="10" column="2" colspan="3">
<widget class="QSpinBox" name="spinThreads"/>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Font monospace:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QFontComboBox" name="fontComboNorm">
<property name="fontFilters">
<set>QFontComboBox::ScalableFonts</set>
</property>
</widget>
</item>
<item row="2" column="3" colspan="2">
<widget class="QSpinBox" name="spinFontSizeNorm">
<property name="minimum">
<number>2</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QFontComboBox" name="fontComboMono">
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="fontFilters">
<set>QFontComboBox::MonospacedFonts</set>
</property>
</widget>
</item>
<item row="3" column="3" colspan="2">
<widget class="QSpinBox" name="spinFontSizeMono">
<property name="minimum">
<number>1</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -10,37 +10,6 @@
#include "cubiomes/quadbase.h"
#include "cubiomes/util.h"
inline const char* struct2str(int stype)
{
switch (stype)
{
case Desert_Pyramid: return QApplication::translate("StructureDialog", "desert_pyramid").toUtf8().data();
case Jungle_Temple: return QApplication::translate("StructureDialog", "jungle_temple").toUtf8().data();
case Swamp_Hut: return QApplication::translate("StructureDialog", "swamp_hut").toUtf8().data();
case Igloo: return QApplication::translate("StructureDialog", "igloo").toUtf8().data();
case Village: return QApplication::translate("StructureDialog", "village").toUtf8().data();
case Ocean_Ruin: return QApplication::translate("StructureDialog", "ocean_ruin").toUtf8().data();
case Shipwreck: return QApplication::translate("StructureDialog", "shipwreck").toUtf8().data();
case Monument: return QApplication::translate("StructureDialog", "monument").toUtf8().data();
case Mansion: return QApplication::translate("StructureDialog", "mansion").toUtf8().data();
case Outpost: return QApplication::translate("StructureDialog", "outpost").toUtf8().data();
case Treasure: return QApplication::translate("StructureDialog", "treasure").toUtf8().data();
case Mineshaft: return QApplication::translate("StructureDialog", "mineshaft").toUtf8().data();
case Desert_Well: return QApplication::translate("StructureDialog", "desert_well").toUtf8().data();
case Ruined_Portal: return QApplication::translate("StructureDialog", "ruined_portal").toUtf8().data();
case Ruined_Portal_N: return QApplication::translate("StructureDialog", "ruined_portal (nether)").toUtf8().data();
case Geode: return QApplication::translate("StructureDialog", "amethyst_geode").toUtf8().data();
case Ancient_City: return QApplication::translate("StructureDialog", "ancient_city").toUtf8().data();
case Trail_Ruin: return QApplication::translate("StructureDialog", "trail_ruins").toUtf8().data();
case Fortress: return QApplication::translate("StructureDialog", "fortress").toUtf8().data();
case Bastion: return QApplication::translate("StructureDialog", "bastion").toUtf8().data();
case End_City: return QApplication::translate("StructureDialog", "end_city").toUtf8().data();
case End_Gateway: return QApplication::translate("StructureDialog", "end_gateway").toUtf8().data();
}
return "?";
}
struct StartPiece
{
int stype;
@ -107,6 +76,142 @@ inline QString getStartPieceName(int stype, const StructureVariant *sv)
return name;
}
static inline QString getBiomeDisplay(int mc, int id)
{
if (mc >= MC_1_18)
{
// a bunch of 'new' biomes in 1.18 actually just got renamed
// (based on their features and biome id conversion when upgrading)
switch (id)
{
case old_growth_birch_forest: return QApplication::translate("Biome", "Old Growth Birch Forest");
case old_growth_pine_taiga: return QApplication::translate("Biome", "Old Growth Pine Taiga");
case old_growth_spruce_taiga: return QApplication::translate("Biome", "Old Growth Spruce Taiga");
case snowy_plains: return QApplication::translate("Biome", "Snowy Plains");
case sparse_jungle: return QApplication::translate("Biome", "Sparse Jungle");
case stony_shore: return QApplication::translate("Biome", "Stony Shore");
case windswept_hills: return QApplication::translate("Biome", "Windswept Hills");
case windswept_forest: return QApplication::translate("Biome", "Windswept Forest");
case windswept_gravelly_hills: return QApplication::translate("Biome", "Windswept Gravelly Hills");
case windswept_savanna: return QApplication::translate("Biome", "Windswept Savanna");
case wooded_badlands: return QApplication::translate("Biome", "Wooded Badlands");
}
}
switch (id)
{
case ocean: return QApplication::translate("Biome", "Ocean");
case plains: return QApplication::translate("Biome", "Plains");
case desert: return QApplication::translate("Biome", "Desert");
case mountains: return QApplication::translate("Biome", "Mountains");
case forest: return QApplication::translate("Biome", "Forest");
case taiga: return QApplication::translate("Biome", "Taiga");
case swamp: return QApplication::translate("Biome", "Swamp");
case river: return QApplication::translate("Biome", "River");
case nether_wastes: return QApplication::translate("Biome", "Nether Wastes");
case the_end: return QApplication::translate("Biome", "The End");
// 10
case frozen_ocean: return QApplication::translate("Biome", "Frozen Ocean");
case frozen_river: return QApplication::translate("Biome", "Frozen River");
case snowy_tundra: return QApplication::translate("Biome", "Snowy Plains");
case snowy_mountains: return QApplication::translate("Biome", "Snowy Mountains");
case mushroom_fields: return QApplication::translate("Biome", "Mushroom Fields");
case mushroom_field_shore: return QApplication::translate("Biome", "Mushroom Field Shore");
case beach: return QApplication::translate("Biome", "Beach");
case desert_hills: return QApplication::translate("Biome", "Desert Hills");
case wooded_hills: return QApplication::translate("Biome", "Wooded Hills");
case taiga_hills: return QApplication::translate("Biome", "Taiga Hills");
// 20
case mountain_edge: return QApplication::translate("Biome", "Mountain Edge");
case jungle: return QApplication::translate("Biome", "Jungle");
case jungle_hills: return QApplication::translate("Biome", "Jungle Hills");
case jungle_edge: return QApplication::translate("Biome", "Jungle Edge");
case deep_ocean: return QApplication::translate("Biome", "Deep Ocean");
case stone_shore: return QApplication::translate("Biome", "Stone Shore");
case snowy_beach: return QApplication::translate("Biome", "Snowy Beach");
case birch_forest: return QApplication::translate("Biome", "Birch Forest");
case birch_forest_hills: return QApplication::translate("Biome", "Birch Forest Hills");
case dark_forest: return QApplication::translate("Biome", "Dark Forest");
// 30
case snowy_taiga: return QApplication::translate("Biome", "Snowy Taiga");
case snowy_taiga_hills: return QApplication::translate("Biome", "Snowy Taiga Hills");
case giant_tree_taiga: return QApplication::translate("Biome", "Giant Tree Taiga");
case giant_tree_taiga_hills: return QApplication::translate("Biome", "Giant Tree Taiga Hills");
case wooded_mountains: return QApplication::translate("Biome", "Wooded Mountains");
case savanna: return QApplication::translate("Biome", "Savanna");
case savanna_plateau: return QApplication::translate("Biome", "Savanna Plateau");
case badlands: return QApplication::translate("Biome", "Badlands");
case wooded_badlands_plateau: return QApplication::translate("Biome", "Wooded Badlands Plateau");
case badlands_plateau: return QApplication::translate("Biome", "Badlands Plateau");
// 40 -- 1.13
case small_end_islands: return QApplication::translate("Biome", "Small End Islands");
case end_midlands: return QApplication::translate("Biome", "End Midlands");
case end_highlands: return QApplication::translate("Biome", "End Highlands");
case end_barrens: return QApplication::translate("Biome", "End Barrens");
case warm_ocean: return QApplication::translate("Biome", "Warm Ocean");
case lukewarm_ocean: return QApplication::translate("Biome", "Lukewarm Ocean");
case cold_ocean: return QApplication::translate("Biome", "Cold Ocean");
case deep_warm_ocean: return QApplication::translate("Biome", "Deep Warm Ocean");
case deep_lukewarm_ocean: return QApplication::translate("Biome", "Deep Lukewarm Ocean");
case deep_cold_ocean: return QApplication::translate("Biome", "Deep Cold Ocean");
// 50
case deep_frozen_ocean: return QApplication::translate("Biome", "Deep Frozen Ocean");
// Alpha 1.2 - Beta 1.7
case seasonal_forest: return QApplication::translate("Biome", "Seasonal Forest");
case shrubland: return QApplication::translate("Biome", "Shrubland");
case rainforest: return QApplication::translate("Biome", "Rain Forest");
case the_void: return QApplication::translate("Biome", "The Void");
// mutated variants
case sunflower_plains: return QApplication::translate("Biome", "Sunflower Plains");
case desert_lakes: return QApplication::translate("Biome", "Desert Lakes");
case gravelly_mountains: return QApplication::translate("Biome", "Gravelly Mountains");
case flower_forest: return QApplication::translate("Biome", "Flower Forest");
case taiga_mountains: return QApplication::translate("Biome", "Taiga Mountains");
case swamp_hills: return QApplication::translate("Biome", "Swamp Hills");
case ice_spikes: return QApplication::translate("Biome", "Ice Spikes");
case modified_jungle: return QApplication::translate("Biome", "Modified Jungle");
case modified_jungle_edge: return QApplication::translate("Biome", "Modified Jungle Edge");
case tall_birch_forest: return QApplication::translate("Biome", "Tall Birch Forest");
case tall_birch_hills: return QApplication::translate("Biome", "Tall Birch Hills");
case dark_forest_hills: return QApplication::translate("Biome", "Dark Forest Hills");
case snowy_taiga_mountains: return QApplication::translate("Biome", "Snowy Taiga Mountains");
case giant_spruce_taiga: return QApplication::translate("Biome", "Giant Spruce Taiga");
case giant_spruce_taiga_hills: return QApplication::translate("Biome", "Giant Spruce Taiga Hills");
case modified_gravelly_mountains: return QApplication::translate("Biome", "Gravelly Mountains+");
case shattered_savanna: return QApplication::translate("Biome", "Shattered Savanna");
case shattered_savanna_plateau: return QApplication::translate("Biome", "Shattered Savanna Plateau");
case eroded_badlands: return QApplication::translate("Biome", "Eroded Badlands");
case modified_wooded_badlands_plateau: return QApplication::translate("Biome", "Modified Wooded Badlands Plateau");
case modified_badlands_plateau: return QApplication::translate("Biome", "Modified Badlands Plateau");
// 1.14
case bamboo_jungle: return QApplication::translate("Biome", "Bamboo Jungle");
case bamboo_jungle_hills: return QApplication::translate("Biome", "Bamboo Jungle Hills");
// 1.16
case soul_sand_valley: return QApplication::translate("Biome", "Soul Sand Valley");
case crimson_forest: return QApplication::translate("Biome", "Crimson Forest");
case warped_forest: return QApplication::translate("Biome", "Warped Forest");
case basalt_deltas: return QApplication::translate("Biome", "Basalt Deltas");
// 1.17
case dripstone_caves: return QApplication::translate("Biome", "Dripstone Caves");
case lush_caves: return QApplication::translate("Biome", "Lush Caves");
// 1.18
case meadow: return QApplication::translate("Biome", "Meadow");
case grove: return QApplication::translate("Biome", "Grove");
case snowy_slopes: return QApplication::translate("Biome", "Snowy Slopes");
case stony_peaks: return QApplication::translate("Biome", "Stony Peaks");
case jagged_peaks: return QApplication::translate("Biome", "Jagged Peaks");
case frozen_peaks: return QApplication::translate("Biome", "Frozen Peaks");
// 1.19
case deep_dark: return QApplication::translate("Biome", "Deep Dark");
case mangrove_swamp: return QApplication::translate("Biome", "Mangrove Swamp");
// 1.20
case cherry_grove: return QApplication::translate("Biome", "Cherry Grove");
}
const char *name = biome2str(mc, id);
return name ? name : "";
}
// get a random 64-bit integer
static inline uint64_t getRnd64()

View File

@ -4,6 +4,7 @@
#include "mainwindow.h"
#include "mapview.h"
#include "cutil.h"
#include "message.h"
#include <QIntValidator>
#include <QFileDialog>
@ -86,7 +87,7 @@ void ExportWorker::run()
static void setCombo(QComboBox *cb, const char *setting)
{
QSettings settings("cubiomes-viewer", "cubiomes-viewer");
QSettings settings(APP_STRING, APP_STRING);
int idx = settings.value(setting, cb->currentIndex()).toInt();
if (idx < cb->count())
cb->setCurrentIndex(idx);
@ -136,7 +137,7 @@ ExportDialog::ExportDialog(MainWindow *parent)
connect(ui->comboTileSize, SIGNAL(activated(int)), this, SLOT(update()));
connect(ui->groupTiled, SIGNAL(toggled(bool)), this, SLOT(update()));
QSettings settings("cubiomes-viewer", "cubiomes-viewer");
QSettings settings(APP_STRING, APP_STRING);
ui->lineDir->setText(settings.value("export/prevdir", mainwindow->prevdir).toString());
ui->linePattern->setText(settings.value("export/pattern", "%S_%x_%z.png").toString());
@ -173,7 +174,7 @@ bool ExportDialog::initWork(ExportWorkItem *work, uint64_t seed, int tx, int tz)
work->tx = tx;
work->tz = tz;
work->fnam = pattern;
work->fnam.replace("%S", QString::number(seed));
work->fnam.replace("%S", QString::number((int64_t)seed));
work->fnam.replace("%x", QString::number(tx));
work->fnam.replace("%z", QString::number(tz));
work->fnam = dir.filePath(work->fnam);
@ -213,7 +214,7 @@ void ExportDialog::update()
if (ui->comboSeed->currentIndex() == 1)
{
const QVector<uint64_t>& seeds = mainwindow->formControl->getResults();
const std::vector<uint64_t>& seeds = mainwindow->formControl->getResults();
seedcnt = seeds.size();
}
@ -313,7 +314,7 @@ void ExportDialog::on_buttonBox_clicked(QAbstractButton *button)
WorldInfo wi;
mainwindow->getSeed(&wi, true);
QVector<uint64_t> seeds;
std::vector<uint64_t> seeds;
if (seedmode == 0)
seeds.push_back(wi.seed);
else if (seedmode == 1)
@ -321,17 +322,15 @@ void ExportDialog::on_buttonBox_clicked(QAbstractButton *button)
if (seeds.size() > 1 && !pattern.contains("%S"))
{
QMessageBox::warning(this, tr("Warning"),
tr("When exporting more than one seed, the file pattern "
"has to include the \"%S\" format specifier for the seed number."));
warn(this, tr("When exporting more than one seed, the file pattern has to "
"include the \"%S\" format specifier for the seed number."));
return;
}
if (tiled && (!pattern.contains("%x") || !pattern.contains("%z")))
{
QMessageBox::warning(this, tr("Warning"),
tr("Exporting as tiled images requires both the \"%x\" and \"%z\" "
"format specifiers in the file pattern, representing the tile coordinates."));
warn(this, tr("Exporting as tiled images requires both the \"%x\" and \"%z\" "
"format specifiers in the file pattern, representing the tile coordinates."));
return;
}
@ -348,7 +347,7 @@ void ExportDialog::on_buttonBox_clicked(QAbstractButton *button)
if (x1 <= x0 || z1 <= z0)
{
QMessageBox::warning(this, tr("Warning"), tr("Invalid area."));
warn(this, tr("Invalid area."));
return;
}
@ -379,7 +378,7 @@ void ExportDialog::on_buttonBox_clicked(QAbstractButton *button)
this->tilesize = tilesize;
this->bgmode = bgmode;
for (uint64_t seed : qAsConst(seeds))
for (uint64_t seed : seeds)
{
for (int x = tx0; x < tx1; x++)
{
@ -397,9 +396,7 @@ void ExportDialog::on_buttonBox_clicked(QAbstractButton *button)
int maxsiz = 0x8000;
if (x1 - x0 >= maxsiz || z1 - z0 >= maxsiz)
{
int button = QMessageBox::warning(this, tr("Warning"),
tr("Consider tiling very large images into smaller sections.\n"
"Continue?"),
int button = warn(this, tr("Consider tiling very large images into smaller sections.\nContinue?"),
QMessageBox::Cancel | QMessageBox::Yes);
if (button == QMessageBox::Cancel)
{
@ -407,7 +404,7 @@ void ExportDialog::on_buttonBox_clicked(QAbstractButton *button)
}
}
for (uint64_t seed : qAsConst(seeds))
for (uint64_t seed : seeds)
{
ExportWorkItem work;
existwarn |= initWork(&work, seed, 0, 0);
@ -417,9 +414,7 @@ void ExportDialog::on_buttonBox_clicked(QAbstractButton *button)
if (existwarn)
{
int button = QMessageBox::warning(this, tr("Warning"),
tr("One or more of files already exist.\n"
"Continue and overwrite?"),
int button = warn(this, tr("One or more of files already exist.\nContinue and overwrite?"),
QMessageBox::Cancel | QMessageBox::Yes);
if (button == QMessageBox::Cancel)
{
@ -427,7 +422,7 @@ void ExportDialog::on_buttonBox_clicked(QAbstractButton *button)
}
}
QSettings settings("cubiomes-viewer", "cubiomes-viewer");
QSettings settings(APP_STRING, APP_STRING);
settings.setValue("export/seedIdx", ui->comboSeed->currentIndex());
settings.setValue("export/prevdir", ui->lineDir->text());
settings.setValue("export/pattern", ui->linePattern->text());

View File

@ -39,8 +39,6 @@ FormConditions::FormConditions(QWidget *parent)
qRegisterMetaType< Condition >("Condition");
qRegisterMetaTypeStreamOperators< Condition >("Condition");
ui->listConditionsFull->setFont(*gp_font_mono);
}
FormConditions::~FormConditions()
@ -52,9 +50,9 @@ QVector<Condition> FormConditions::getConditions() const
{
QVector<Condition> conds;
for (int i = 0, ie = ui->listConditionsFull->count(); i < ie; i++)
for (int i = 0, ie = ui->listConditions->count(); i < ie; i++)
{
Condition c = qvariant_cast<Condition>(ui->listConditionsFull->item(i)->data(Qt::UserRole));
Condition c = qvariant_cast<Condition>(ui->listConditions->item(i)->data(Qt::UserRole));
conds.push_back(c);
}
return conds;
@ -64,7 +62,7 @@ void FormConditions::updateSensitivity()
{
if (!parent)
return;
QList<QListWidgetItem*> selected = ui->listConditionsFull->selectedItems();
QList<QListWidgetItem*> selected = ui->listConditions->selectedItems();
if (selected.size() == 0)
{
@ -155,7 +153,7 @@ void FormConditions::setItemCondition(QListWidget *list, QListWidgetItem *item,
cond->save = getIndex(cond->save);
}
QString s = cond->summary();
QString s = cond->summary(&ui->listConditions->font());
const FilterInfo& ft = g_filterinfo.list[cond->type];
if (ft.cat == CAT_QUAD)
@ -189,25 +187,25 @@ static void remove_selected(QListWidget *list)
void FormConditions::on_buttonRemoveAll_clicked()
{
ui->listConditionsFull->clear();
ui->listConditions->clear();
emit changed();
}
void FormConditions::on_buttonRemove_clicked()
{
remove_selected(ui->listConditionsFull);
remove_selected(ui->listConditions);
emit changed();
}
void FormConditions::on_buttonDisable_clicked()
{
emit changed();
QList<QListWidgetItem*> selected = ui->listConditionsFull->selectedItems();
QList<QListWidgetItem*> selected = ui->listConditions->selectedItems();
for (QListWidgetItem *item : selected)
{
Condition c = qvariant_cast<Condition>(item->data(Qt::UserRole));
c.meta ^= Condition::DISABLED;
item->setText(c.summary());
item->setText(c.summary(&font()));
item->setData(Qt::UserRole, QVariant::fromValue(c));
}
updateSensitivity();
@ -216,7 +214,7 @@ void FormConditions::on_buttonDisable_clicked()
void FormConditions::on_buttonEdit_clicked()
{
QListWidgetItem* edit = 0;
QList<QListWidgetItem*> selected = ui->listConditionsFull->selectedItems();
QList<QListWidgetItem*> selected = ui->listConditions->selectedItems();
if (!selected.empty())
edit = lockItem(selected.first());
@ -236,13 +234,13 @@ void FormConditions::on_buttonAddFilter_clicked()
dialog->show();
}
void FormConditions::on_listConditionsFull_customContextMenuRequested(const QPoint &pos)
void FormConditions::on_listConditions_customContextMenuRequested(const QPoint &pos)
{
QMenu menu(this);
// this is a contextual temporary menu so shortcuts are only indicated here,
// but will not function - see keyReleaseEvent() for shortcut implementation
int n = ui->listConditionsFull->selectedItems().size();
int n = ui->listConditions->selectedItems().size();
if (parent)
{
@ -285,17 +283,17 @@ void FormConditions::on_listConditionsFull_customContextMenuRequested(const QPoi
actcopy->setEnabled(n > 0);
}
menu.exec(ui->listConditionsFull->mapToGlobal(pos));
menu.exec(ui->listConditions->mapToGlobal(pos));
}
void FormConditions::on_listConditionsFull_itemDoubleClicked(QListWidgetItem *item)
void FormConditions::on_listConditions_itemDoubleClicked(QListWidgetItem *item)
{
if (!parent)
return;
editCondition(lockItem(item));
}
void FormConditions::on_listConditionsFull_itemSelectionChanged()
void FormConditions::on_listConditions_itemSelectionChanged()
{
updateSensitivity();
}
@ -325,7 +323,7 @@ void FormConditions::conditionsCut()
void FormConditions::conditionsCopy()
{
QString text;
QList<QListWidgetItem*> selected = ui->listConditionsFull->selectedItems();
QList<QListWidgetItem*> selected = ui->listConditions->selectedItems();
for (QListWidgetItem *item : selected)
{
Condition c = qvariant_cast<Condition>(item->data(Qt::UserRole));
@ -372,17 +370,17 @@ void FormConditions::addItemCondition(QListWidgetItem *item, Condition cond, int
item = new QListWidgetItem();
modified = 1;
}
setItemCondition(ui->listConditionsFull, item, &cond);
setItemCondition(ui->listConditions, item, &cond);
}
else if (item)
{
setItemCondition(ui->listConditionsFull, item, &cond);
setItemCondition(ui->listConditions, item, &cond);
}
else
{
modified = 1;
item = new QListWidgetItem();
setItemCondition(ui->listConditionsFull, item, &cond);
setItemCondition(ui->listConditions, item, &cond);
if (cond.type >= F_QH_IDEAL && cond.type <= F_QH_BARELY)
{
@ -396,8 +394,8 @@ void FormConditions::addItemCondition(QListWidgetItem *item, Condition cond, int
cq.relative = cond.save;
cq.save = cond.save+1;
cq.count = 4;
QListWidgetItem *item = new QListWidgetItem(ui->listConditionsFull, QListWidgetItem::UserType);
setItemCondition(ui->listConditionsFull, item, &cq);
QListWidgetItem *item = new QListWidgetItem(ui->listConditions, QListWidgetItem::UserType);
setItemCondition(ui->listConditions, item, &cq);
}
else if (cond.type == F_QM_90 || cond.type == F_QM_95)
{
@ -411,8 +409,8 @@ void FormConditions::addItemCondition(QListWidgetItem *item, Condition cond, int
cq.relative = cond.save;
cq.save = cond.save+1;
cq.count = 4;
QListWidgetItem *item = new QListWidgetItem(ui->listConditionsFull, QListWidgetItem::UserType);
setItemCondition(ui->listConditionsFull, item, &cq);
QListWidgetItem *item = new QListWidgetItem(ui->listConditions, QListWidgetItem::UserType);
setItemCondition(ui->listConditions, item, &cq);
}
}
@ -420,14 +418,14 @@ void FormConditions::addItemCondition(QListWidgetItem *item, Condition cond, int
emit changed();
}
void FormConditions::on_listConditionsFull_indexesMoved(const QModelIndexList &)
void FormConditions::on_listConditions_indexesMoved(const QModelIndexList &)
{
emit changed();
}
void FormConditions::keyReleaseEvent(QKeyEvent *event)
{
if (ui->listConditionsFull->hasFocus())
if (ui->listConditions->hasFocus())
{
if (event->matches(QKeySequence::New))
conditionsAdd();

View File

@ -40,9 +40,9 @@ public slots:
void on_buttonEdit_clicked();
void on_buttonAddFilter_clicked();
void on_listConditionsFull_customContextMenuRequested(const QPoint &pos);
void on_listConditionsFull_itemDoubleClicked(QListWidgetItem *item);
void on_listConditionsFull_itemSelectionChanged();
void on_listConditions_customContextMenuRequested(const QPoint &pos);
void on_listConditions_itemDoubleClicked(QListWidgetItem *item);
void on_listConditions_itemSelectionChanged();
void conditionsAdd();
void conditionsEdit();
@ -54,7 +54,7 @@ public slots:
void addItemCondition(QListWidgetItem *item, Condition cond, int modified = 1);
private slots:
void on_listConditionsFull_indexesMoved(const QModelIndexList &indexes);
void on_listConditions_indexesMoved(const QModelIndexList &indexes);
protected:
void keyReleaseEvent(QKeyEvent *event) override;

View File

@ -30,7 +30,7 @@
<number>0</number>
</property>
<item row="0" column="0" colspan="5">
<widget class="QListWidget" name="listConditionsFull">
<widget class="QListWidget" name="listConditions">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>

View File

@ -4,12 +4,12 @@
#include "mainwindow.h"
#include "search.h"
#include "seedtables.h"
#include "message.h"
#include "cubiomes/util.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QMessageBox>
#include <QSpinBox>
#include <QList>
@ -79,8 +79,6 @@ FormGen48::FormGen48(MainWindow *parent)
connect(ui->lineSalt, SIGNAL(editingFinished()), SLOT(onChange()));
connect(ui->lineListSalt, SIGNAL(editingFinished()), SLOT(onChange()));
ui->lineList48->setFont(*gp_font_mono);
cond.type = 0;
Gen48Config defaults;
setConfig(defaults, true);
@ -114,10 +112,8 @@ bool FormGen48::setList48(QString path, bool quiet)
}
else if (!quiet)
{
int button = QMessageBox::warning(
this, tr("Warning"),
tr("Failed to load 48-bit seed list from file:\n\"%1\"").arg(path),
QMessageBox::Reset, QMessageBox::Ignore);
int button = warn(this, tr("Failed to load 48-bit seed list from file:\n\"%1\"").arg(path),
QMessageBox::Reset|QMessageBox::Ignore);
if (button == QMessageBox::Reset)
{
slist48path.clear();
@ -205,48 +201,7 @@ Gen48Config FormGen48::getConfig(bool resolveauto)
uint64_t FormGen48::estimateSeedCnt()
{
const Gen48Config& gen48 = getConfig(true);
uint64_t cnt = 0;
if (gen48.mode == GEN48_QH)
{
switch (gen48.qual)
{ // simply hardcoded number of seeds in each category
case IDEAL_SALTED: // falltrough
case IDEAL: cnt = 74474; break;
case CLASSIC: cnt = 107959; break;
case NORMAL: cnt = 293716; break;
case BARELY: cnt = 755745; break;
default: cnt = 0;
}
}
else if (gen48.mode == GEN48_QM)
{
cnt = 0;
for (int i = 0, n = sizeof(g_qm_90) / sizeof(int64_t); i < n; i++)
if (qmonumentQual(g_qm_90[i]) >= gen48.qmarea)
cnt++;
}
else if (gen48.mode == GEN48_LIST)
{
cnt = this->slist48.size();
}
else
{
cnt = MASK48 + 1;
}
if (gen48.mode != GEN48_NONE)
{
uint64_t w = gen48.x2 - gen48.x1 + 1;
uint64_t h = gen48.z2 - gen48.z1 + 1;
uint64_t n = w*h * cnt;
if (cnt > 0 && n < PRECOMPUTE48_BUFSIZ*sizeof(uint64_t) && n / cnt == w*h)
cnt = n;
else
cnt = MASK48+1;
}
return cnt;
return getConfig(true).estimateSeedCnt(slist48.size());
}
void FormGen48::updateCount()

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>435</width>
<height>182</height>
<height>188</height>
</rect>
</property>
<property name="sizePolicy">
@ -20,26 +20,53 @@
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QWidget" name="widget" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0" colspan="2">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
<number>3</number>
</property>
<widget class="QWidget" name="tabAuto">
<attribute name="title">
<string>Auto/None</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<property name="margin">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item row="0" column="0">
@ -63,7 +90,16 @@
<string>Quad-feature</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_5">
<property name="margin">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item row="1" column="0">
@ -154,7 +190,16 @@ Applies only to feature-structures of region-size = 32 and chunk-gap = 8, in par
<string>Seed list</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_7">
<property name="margin">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item row="0" column="0">
@ -172,7 +217,6 @@ Applies only to feature-structures of region-size = 32 and chunk-gap = 8, in par
<property name="font">
<font>
<family>Monospace</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="readOnly">
@ -226,7 +270,16 @@ Applies only to feature-structures of region-size = 32 and chunk-gap = 8, in par
<enum>QFrame::Plain</enum>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="1">

View File

@ -4,6 +4,7 @@
#include "mainwindow.h"
#include "search.h"
#include "rangedialog.h"
#include "message.h"
#include "cubiomes/util.h"
@ -99,9 +100,9 @@ FormSearchControl::FormSearchControl(MainWindow *parent)
, proxy(new SeedSortProxy(this))
, protodialog()
, sthread(this)
, stimer()
, elapsed()
, proghist()
, stimer()
, resultfile()
, slist64path()
, slist64fnam()
, slist64()
@ -114,17 +115,12 @@ FormSearchControl::FormSearchControl(MainWindow *parent)
ui->setupUi(this);
protodialog = new ProtoBaseDialog(this);
QFont mono = *gp_font_mono;
ui->results->setFont(mono);
ui->progressBar->setFont(mono);
ui->labelStatus->setFont(mono);
proxy->setSourceModel(model);
ui->results->setModel(proxy);
ui->results->horizontalHeader()->setFont(mono);
ui->results->horizontalHeader()->setFont(ui->results->font());
ui->results->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
ui->results->verticalHeader()->setDefaultSectionSize(QFontMetrics(mono).height());
ui->results->verticalHeader()->setDefaultSectionSize(QFontMetrics(ui->results->font()).height());
ui->results->setColumnWidth(SeedTableModel::COL_SEED, 200);
ui->results->setColumnWidth(SeedTableModel::COL_TOP16, 60);
ui->results->setColumnWidth(SeedTableModel::COL_HEX48, 120);
@ -132,9 +128,10 @@ FormSearchControl::FormSearchControl(MainWindow *parent)
connect(ui->results->horizontalHeader(), &QHeaderView::sortIndicatorChanged, this, &FormSearchControl::onSort);
ui->results->sortByColumn(-1, Qt::AscendingOrder);
connect(&sthread, &SearchMaster::searchResult, this, &FormSearchControl::searchResult, Qt::QueuedConnection);
connect(&sthread, &SearchMaster::searchFinish, this, &FormSearchControl::searchFinish, Qt::QueuedConnection);
connect(&stimer, &QTimer::timeout, this, QOverload<>::of(&FormSearchControl::resultTimeout));
connect(&stimer, &QTimer::timeout, this, QOverload<>::of(&FormSearchControl::progressTimeout));
connect(
ui->results->selectionModel(),
@ -157,10 +154,10 @@ FormSearchControl::~FormSearchControl()
delete ui;
}
QVector<uint64_t> FormSearchControl::getResults()
std::vector<uint64_t> FormSearchControl::getResults()
{
int n = proxy->rowCount();
QVector<uint64_t> results = QVector<uint64_t>(n);
std::vector<uint64_t> results (n);
for (int i = 0; i < n; i++)
{
results[i] = proxy->data(proxy->index(i, SeedTableModel::COL_SEED), Qt::UserRole).toULongLong();
@ -229,16 +226,14 @@ bool FormSearchControl::setList64(QString path, bool quiet)
if (l != NULL)
{
slist64.assign(l, l+len);
searchProgress(0, len, l[0]);
updateSearchProgress(0, len, l[0]);
free(l);
return true;
}
else if (!quiet)
{
int button = QMessageBox::warning(
this, tr("Warning"),
tr("Failed to load 64-bit seed list from file:\n\"%1\"").arg(path),
QMessageBox::Reset, QMessageBox::Ignore);
int button = warn(this, tr("Failed to load 64-bit seed list from file:\n\"%1\"").arg(path),
QMessageBox::Reset|QMessageBox::Ignore);
if (button == QMessageBox::Reset)
{
slist64fnam.clear();
@ -250,6 +245,12 @@ bool FormSearchControl::setList64(QString path, bool quiet)
return false;
}
void FormSearchControl::setResultsPath(QString path)
{
resultfile.close();
resultfile.setFileName(path);
}
void FormSearchControl::searchLockUi(bool lock)
{
if (lock)
@ -290,7 +291,7 @@ void FormSearchControl::setSearchMode(int mode)
int FormSearchControl::warning(QString text, QMessageBox::StandardButtons buttons)
{
return QMessageBox::warning(this, tr("Warning"), text, buttons);
return warn(this, text, buttons);
}
void FormSearchControl::openProtobaseMsg(QString path)
@ -316,46 +317,52 @@ void FormSearchControl::on_buttonStart_clicked()
{
if (ui->buttonStart->isChecked())
{
WorldInfo wi;
parent->getSeed(&wi);
const Config& config = parent->config;
QVector<Condition> condvec = parent->formCond->getConditions();
SearchConfig sc = getSearchConfig();
Session session;
parent->getSeed(&session.wi);
session.cv = parent->formCond->getConditions();
session.sc = getSearchConfig();
int ok = true;
if (condvec.empty())
if (session.cv.empty())
{
QMessageBox::warning(this, tr("Warning"), tr("Please define some constraints using the \"Add\" button."), QMessageBox::Ok);
warn(this, tr("Please define some constraints using the \"Add\" button."));
ok = false;
}
if (sc.searchtype == SEARCH_LIST && slist64.empty())
if (session.sc.searchtype == SEARCH_LIST && slist64.empty())
{
QMessageBox::warning(this, tr("Warning"), tr("No seed list file selected."), QMessageBox::Ok);
warn(this, tr("No seed list file selected."));
ok = false;
}
if (sthread.isRunning())
{
QMessageBox::warning(this, tr("Warning"), tr("Search is still running."), QMessageBox::Ok);
warn(this, tr("Search is still running."));
ok = false;
}
if (ok)
{
Gen48Config gen48 = parent->formGen48->getConfig(true);
session.gen48 = parent->formGen48->getConfig(true);
// the search can either use a full list or a 48-bit list
if (sc.searchtype == SEARCH_LIST)
slist = slist64;
else if (gen48.mode == GEN48_LIST)
slist = parent->formGen48->getList48();
if (session.sc.searchtype == SEARCH_LIST)
session.slist = slist64;
else if (session.gen48.mode == GEN48_LIST)
session.slist = parent->formGen48->getList48();
else
slist.clear();
session.slist.clear();
ok = sthread.set(wi, sc, gen48, config, slist, condvec);
ok = sthread.set(parent, session);
}
if (ok)
{
ui->lineStart->setText(QString::asprintf("%" PRId64, (int64_t)sc.startseed));
if (!resultfile.fileName().isEmpty())
{
resultfile.close();
resultfile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text);
QTextStream stream(&resultfile);
session.writeHeader(stream);
}
ui->lineStart->setText(QString::asprintf("%" PRId64, (int64_t)session.sc.startseed));
ui->buttonStart->setText(tr("Abort search"));
ui->buttonStart->setIcon(QIcon(":/icons/cancel.png"));
searchLockUi(true);
@ -431,12 +438,17 @@ void FormSearchControl::on_results_customContextMenuRequested(const QPoint &pos)
QAction *actremove = menu.addAction(QIcon::fromTheme("list-remove"),
tr("Remove selected seed"), this,
&FormSearchControl::removeCurrent, QKeySequence::Delete);
actremove->setEnabled(!ui->results->selectionModel()->hasSelection());
actremove->setEnabled(ui->results->selectionModel()->hasSelection());
QAction *actcopy = menu.addAction(QIcon::fromTheme("edit-copy"),
tr("Copy list to clipboard"), this,
&FormSearchControl::copyResults, QKeySequence::Copy);
actcopy->setEnabled(ui->results->model()->rowCount() > 0);
QAction *actcopyseed = menu.addAction(QIcon::fromTheme("edit-copy"),
tr("Copy selected seed"), this,
&FormSearchControl::copySeed, QKeySequence::Copy);
actcopyseed->setEnabled(ui->results->selectionModel()->hasSelection());
QAction *actcopylist = menu.addAction(QIcon::fromTheme("edit-copy"),
tr("Copy seed list"), this,
&FormSearchControl::copyResults, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_C));
actcopylist->setEnabled(ui->results->model()->rowCount() > 0);
int n = pasteList(true);
QAction *actpaste = menu.addAction(QIcon::fromTheme("edit-paste"),
@ -489,9 +501,9 @@ int FormSearchControl::pasteList(bool dummy)
{
QClipboard *clipboard = QGuiApplication::clipboard();
QStringList slist = clipboard->text().split('\n');
QVector<uint64_t> seeds;
std::vector<uint64_t> seeds;
for (QString s : slist)
for (QString s : qAsConst(slist))
{
s = s.trimmed();
if (s.isEmpty())
@ -532,8 +544,16 @@ void FormSearchControl::onSort(int, Qt::SortOrder)
}
}
void FormSearchControl::onSearchResult(uint64_t seed)
void FormSearchControl::searchResult(uint64_t seed)
{
if (resultfile.isOpen())
{
char s[32];
snprintf(s, sizeof(s), "%" PRId64 "\n", seed);
resultfile.write(s);
resultfile.flush();
}
qbuf.push_back(seed);
if (ui->checkStop->isChecked())
{
@ -564,14 +584,14 @@ void FormSearchControl::onBufferTimeout()
nextupdate = elapsed.nsecsElapsed() + 1e6 * updt;
}
int FormSearchControl::searchResultsAdd(QVector<uint64_t> seeds, bool countonly)
int FormSearchControl::searchResultsAdd(std::vector<uint64_t> seeds, bool countonly)
{
const Config& config = parent->config;
int ns = model->seeds.size();
int n = ns;
if (n >= config.maxMatching)
return 0;
if (seeds.size() + n > config.maxMatching)
if ((ssize_t)seeds.size() + n > config.maxMatching)
seeds.resize(config.maxMatching - n);
if (seeds.empty())
return 0;
@ -604,8 +624,7 @@ int FormSearchControl::searchResultsAdd(QVector<uint64_t> seeds, bool countonly)
if (countonly == false && n >= config.maxMatching)
{
sthread.stop();
QString msg = tr("Maximum number of results reached (%1).").arg(config.maxMatching);
QMessageBox::warning(this, tr("Warning"), msg, QMessageBox::Ok);
warn(this, tr("Maximum number of results reached (%1).").arg(config.maxMatching));
}
int addcnt = n - ns;
@ -620,8 +639,7 @@ int FormSearchControl::searchResultsAdd(QVector<uint64_t> seeds, bool countonly)
void FormSearchControl::searchProgressReset()
{
uint64_t cnt;
cnt = parent->formGen48->estimateSeedCnt();
uint64_t cnt = parent->formGen48->estimateSeedCnt();
if (cnt > MASK48)
cnt = ~(uint64_t)0;
else
@ -655,7 +673,7 @@ void FormSearchControl::searchProgressReset()
ui->progressBar->setFormat(fmt);
}
void FormSearchControl::searchProgress(uint64_t prog, uint64_t end, int64_t seed)
void FormSearchControl::updateSearchProgress(uint64_t prog, uint64_t end, int64_t seed)
{
ui->lineStart->setText(QString::asprintf("%" PRId64, seed));
@ -689,112 +707,27 @@ void FormSearchControl::searchProgress(uint64_t prog, uint64_t end, int64_t seed
void FormSearchControl::searchFinish(bool done)
{
stimer.stop();
resultTimeout();
progressTimeout();
if (done)
{
ui->lineStart->setText(QString::asprintf("%" PRId64, sthread.smax));
ui->progressBar->setValue(10000);
ui->progressBar->setFormat(tr("Done", "Progressbar when finished"));
ui->progressBar->setFormat(tr("Done", "Progressbar"));
}
ui->labelStatus->setText(tr("Idle"));
proghist.clear();
ui->labelStatus->setText(tr("Idle", "Progressbar"));
searchLockUi(false);
}
#define SAMPLE_SEC 20
static void estmateSpeed(const std::deque<FormSearchControl::TProg>& hist,
double *min, double *avg, double *max)
{ // We will try to get a decent estimate for the search speed.
std::vector<double> samples;
samples.reserve(hist.size());
auto it_last = hist.begin();
auto it = it_last;
while (++it != hist.end())
{
double dp = it_last->prog - it->prog;
double dt = 1e-9 * (it_last->ns - it->ns);
if (dt > 0)
samples.push_back(dp / dt);
it_last = it;
}
std::sort(samples.begin(), samples.end());
double speedtot = 0;
double weightot = 1e-6;
int n = (int) samples.size();
int r = (int) (n / SAMPLE_SEC);
int has_zeros = 0;
for (int i = -r; i <= r; i++)
{
int j = n/2 + i;
if (j < 0 || j >= n)
continue;
has_zeros += samples[j] == 0;
speedtot += samples[j];
weightot += 1.0;
}
if (min) *min = samples[n*1/4]; // lower quartile
if (avg) *avg = speedtot / weightot; // median
if (max) *max = samples[n*3/4]; // upper quartile
if (avg && has_zeros)
{ // probably a slow sampling regime, use whole range for estimate
speedtot = 0;
for (double s : samples)
speedtot += s;
*avg = speedtot / n;
}
}
static QString getAbbrNum(double x)
{
if (x >= 10e9)
return QString::asprintf("%.1fG", x * 1e-9);
if (x >= 10e6)
return QString::asprintf("%.1fM", x * 1e-6);
if (x >= 10e3)
return QString::asprintf("%.1fK", x * 1e-3);
return QString::asprintf("%.2f", x);
}
void FormSearchControl::resultTimeout()
void FormSearchControl::progressTimeout()
{
uint64_t prog, end, seed;
if (!sthread.getProgress(&prog, &end, &seed))
qreal min, avg, max;
QString status = tr("Running...", "Progressbar");
if (!sthread.getProgress(&status, &prog, &end, &seed, &min, &avg, &max))
return;
searchProgress(prog, end, seed);
// track the progress over a few seconds so we can estimate the search speed
TProg tp = { (uint64_t) elapsed.nsecsElapsed(), prog };
proghist.push_front(tp);
while (proghist.size() > 1 && proghist.back().ns < tp.ns - SAMPLE_SEC*1e9)
proghist.pop_back();
updateSearchProgress(prog, end, seed);
QString status = tr("Running...");
if (proghist.size() > 1 && proghist.front().ns > proghist.back().ns)
{
double min, avg, max;
estmateSpeed(proghist, &min, &avg, &max);
double remain = ((double)end - prog) / (avg + 1e-6);
QString eta;
if (remain >= 3600*24*1000)
eta = "years";
else
{
int s = (int) remain;
if (s > 86400)
eta = QString("%1d:%2").arg(s / 86400).arg((s % 86400) / 3600, 2, 10, QLatin1Char('0'));
else if (s > 3600)
eta = QString("%1h:%2").arg(s / 3600).arg((s % 3600) / 60, 2, 10, QLatin1Char('0'));
else
eta = QString("%1:%2").arg(s / 60).arg(s % 60, 2, 10, QLatin1Char('0'));
}
status = tr("seeds/sec: %1 min: %2 max: %3 isize: %4 eta: %5")
.arg(getAbbrNum(avg), -8)
.arg(getAbbrNum(min), -8)
.arg(getAbbrNum(max), -8)
.arg(sthread.itemsize, -3)
.arg(eta);
}
ui->labelStatus->setText(status);
update();
@ -810,6 +743,17 @@ void FormSearchControl::removeCurrent()
}
}
void FormSearchControl::copySeed()
{
QModelIndex index = ui->results->currentIndex();
if (index.isValid())
{
uint64_t seed = ui->results->model()->data(index, Qt::UserRole).toULongLong();
QClipboard *clipboard = QGuiApplication::clipboard();
clipboard->setText(QString::asprintf("%" PRId64, seed));
}
}
void FormSearchControl::copyResults()
{
QString text;
@ -832,6 +776,8 @@ void FormSearchControl::keyReleaseEvent(QKeyEvent *event)
if (event->matches(QKeySequence::Delete))
removeCurrent();
else if (event->matches(QKeySequence::Copy))
copySeed();
else if ((event->modifiers() & Qt::CTRL) && (event->modifiers() & Qt::SHIFT) && event->key() == Qt::Key_C)
copyResults();
else if (event->matches(QKeySequence::Paste))
pasteResults();

View File

@ -98,13 +98,15 @@ public:
explicit FormSearchControl(MainWindow *parent);
~FormSearchControl();
QVector<uint64_t> getResults();
std::vector<uint64_t> getResults();
SearchConfig getSearchConfig();
bool setSearchConfig(SearchConfig s, bool quiet);
void stopSearch();
bool setList64(QString path, bool quiet);
void setResultsPath(QString path);
void searchLockUi(bool lock);
void setSearchMode(int mode);
@ -135,21 +137,19 @@ public slots:
void pasteResults();
int pasteList(bool dummy);
void onBufferTimeout();
void onSearchResult(uint64_t seed);
int searchResultsAdd(QVector<uint64_t> seeds, bool countonly);
void searchResult(uint64_t seed);
int searchResultsAdd(std::vector<uint64_t> seeds, bool countonly);
void searchProgressReset();
void searchProgress(uint64_t last, uint64_t end, int64_t seed);
void updateSearchProgress(uint64_t last, uint64_t end, int64_t seed);
void searchFinish(bool done);
void resultTimeout();
void progressTimeout();
void removeCurrent();
void copySeed();
void copyResults();
protected:
void keyReleaseEvent(QKeyEvent *event) override;
public:
struct TProg { uint64_t ns, prog; };
private:
MainWindow *parent;
Ui::FormSearchControl *ui;
@ -157,23 +157,20 @@ private:
SeedSortProxy *proxy;
ProtoBaseDialog *protodialog;
SearchMaster sthread;
QTimer stimer;
QElapsedTimer elapsed;
std::deque<TProg> proghist;
QTimer stimer;
QFile resultfile;
// the seed list option is not stored in a widget but is loaded with the "..." button
QString slist64path;
QString slist64fnam; // file name without directory
std::vector<uint64_t> slist64;
// buffer for seed candidates while search is running
std::vector<uint64_t> slist;
// min and max seeds values
uint64_t smin, smax;
// found seeds that are waiting to be added to results
QVector<uint64_t> qbuf;
std::vector<uint64_t> qbuf;
quint64 nextupdate;
quint64 updt;
};

View File

@ -162,6 +162,11 @@ QPushButton:hover {
</item>
<item row="3" column="0" colspan="7">
<widget class="QProgressBar" name="progressBar">
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="toolTip">
<string>Queued progress within search set</string>
</property>
@ -212,8 +217,7 @@ QPushButton:hover {
<widget class="QLabel" name="labelStatus">
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<pointsize>9</pointsize>
<family>Monospace</family>
</font>
</property>
<property name="text">
@ -239,8 +243,7 @@ QPushButton:hover {
</property>
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<pointsize>9</pointsize>
<family>Monospace</family>
</font>
</property>
<property name="contextMenuPolicy">

View File

@ -2,11 +2,11 @@
#include "ui_gotodialog.h"
#include "mapview.h"
#include "message.h"
#include <QDoubleValidator>
#include <QKeyEvent>
#include <QClipboard>
#include <QMessageBox>
static bool g_animate;
@ -46,9 +46,8 @@ void GotoDialog::on_buttonBox_clicked(QAbstractButton *button)
qreal scale = ui->lineScale->text().toDouble();
if (scale > 4096)
{
int button = QMessageBox::warning(this, tr("Unsafe Scale"),
tr("Setting a very large scale may be unsafe.\n"
"Continue anyway?"), QMessageBox::Abort|QMessageBox::Yes);
int button = warn(this, tr("Setting a very large scale may be unsafe.\n"
"Continue anyway?"), QMessageBox::Abort|QMessageBox::Yes);
if (button == QMessageBox::Abort)
return;
}
@ -69,7 +68,7 @@ void GotoDialog::on_buttonBox_clicked(QAbstractButton *button)
}
}
void GotoDialog::keyReleaseEvent(QKeyEvent *event)
void GotoDialog::keyPressEvent(QKeyEvent *event)
{
if (event->matches(QKeySequence::Paste))
{

View File

@ -17,7 +17,7 @@ public:
explicit GotoDialog(MapView *map, qreal x, qreal z, qreal scale);
~GotoDialog();
void keyReleaseEvent(QKeyEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
private slots:
void on_buttonBox_clicked(QAbstractButton *button);

211
src/headless.cpp Normal file
View File

@ -0,0 +1,211 @@
#include "headless.h"
#include "message.h"
#include "aboutdialog.h"
#include <QStandardPaths>
#include <QApplication>
#include <QDateTime>
#include "cubiomes/util.h"
#if defined(_WIN32)
#include <windows.h>
short get_term_width()
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
return 80;
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
}
#else
#include <sys/ioctl.h>
short get_term_width()
{
struct winsize ws;
if (ioctl(0, TIOCGWINSZ, &ws) < 0)
return 80;
return ws.ws_col;
}
#endif
static QTextStream& qOut()
{
static QTextStream out (stdout);
return out;
}
Headless::Headless(QString sessionpath, QString resultspath)
: QObject(nullptr)
, sthread(nullptr)
, sessionpath(sessionpath)
, resultfile(resultspath)
, resultstream(stdout)
{
sthread.isdone = true;
QTimer::singleShot(0, this, SLOT(start()));
QSettings settings(APP_STRING, APP_STRING);
g_extgen.load(settings);
if (!loadSession(sessionpath))
return;
if (!sthread.set(nullptr, session))
return;
connect(&sthread, &SearchMaster::searchResult, this, &Headless::searchResult, Qt::QueuedConnection);
connect(&sthread, &SearchMaster::searchFinish, this, &Headless::searchFinish, Qt::QueuedConnection);
connect(&timer, &QTimer::timeout, this, QOverload<>::of(&Headless::progressTimeout));
if (!resultfile.fileName().isEmpty())
{
if (resultfile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text))
resultstream.setDevice(&resultfile);
else
warn(nullptr, "Output file for results coult not be created - using stdout instead.");
}
}
Headless::~Headless()
{
}
static bool load_seeds(std::vector<uint64_t>& seeds, QString path)
{
QByteArray ba = path.toLocal8Bit();
uint64_t len;
if (uint64_t *l = loadSavedSeeds(ba.data(), &len))
{
seeds.assign(l, l+len);
free(l);
return true;
}
return false;
}
bool Headless::loadSession(QString sessionpath)
{
qOut() << "Loading session: \"" << sessionpath << "\"\n";
qOut().flush();
if (!session.load(nullptr, sessionpath, false))
{
return false;
}
if (session.cv.empty())
{
warn(nullptr, "Session defines no search constraints.");
return false;
}
if (session.sc.searchtype == SEARCH_LIST)
{
if (!load_seeds(session.slist, session.sc.slist64path))
{
warn(nullptr, QString("Failed to load 64-bit seed list:\n\"%1\"").arg(session.sc.slist64path));
return false;
}
}
else if (session.gen48.mode == GEN48_LIST)
{
if (!load_seeds(session.slist, session.gen48.slist48path))
{
warn(nullptr, QString("Failed to load 48-bit seed list:\n\"%1\"").arg(session.gen48.slist48path));
return false;
}
}
else
{
session.slist.clear();
}
return true;
}
void Headless::start()
{
qOut() << "Condition summary:\n";
for (const Condition& cond : qAsConst(session.cv))
qOut() << cond.summary(nullptr) << "\n";
if (sthread.isdone)
{
qOut() << "Search parameters invalid or incomplete.\n";
qOut().flush();
searchFinish(false);
return;
}
qOut() << "\nSearching for seeds...\n\n";
qOut().flush();
session.writeHeader(resultstream);
resultstream.flush();
sthread.start();
if (resultfile.isOpen())
{
qOut() << "\n\n\n\n\n\n";
qOut().flush();
timer.start(250);
}
}
void Headless::searchResult(uint64_t seed)
{
results.push_back(seed);
resultstream << (int64_t) seed << "\n";
resultstream.flush();
}
void Headless::searchFinish(bool done)
{
if (timer.isActive())
{
timer.stop();
progressTimeout();
}
if (done)
qOut() << "Search done!\n";
qOut() << "Stopping event loop.\n";
qOut().flush();
QApplication::quit();
}
void Headless::progressTimeout()
{
QString status;
uint64_t prog, end, seed;
qreal min, avg, max;
sthread.getProgress(&status, &prog, &end, &seed, &min, &avg, &max);
short width = get_term_width();
if (width <= 24)
return;
qreal perc = (qreal) prog / end;
int cols = floor(perc * (width - 4) + 1e-6);
QStringList l;
l += QString(" Found matching seeds:%1 ").arg(results.size(), width-23);
l += QString(" Scheduled seed:%1 ").arg((int64_t)seed, width-17);
l += QString(" Progress:%1 ").arg(QString("%1 / %2 : %3%").arg(prog).arg(end).arg(100*perc, 5, 'f', 2), width-11);
l += QString(" [%1%2] ").arg("", cols, '#').arg("", width-cols-4, '-');
l += QString(" %1").arg(status, 1-width);
l += "";
qOut() << "\e[999D\e[" << l.size() << "A";
for (QString& s : l)
{
s.truncate(width);
qOut() << s << "\n";
}
qOut().flush();
}

34
src/headless.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef HEADLESS_H
#define HEADLESS_H
#include "searchthread.h"
#include <QTimer>
#include <QFile>
class Headless : public QObject
{
Q_OBJECT
public:
Headless(QString sessionpath, QString resultspath);
virtual ~Headless();
bool loadSession(QString sessionpath);
public slots:
void start();
void searchResult(uint64_t seed);
void searchFinish(bool done);
void progressTimeout();
public:
SearchMaster sthread;
QString sessionpath;
Session session;
std::vector<uint64_t> results;
QFile resultfile;
QTextStream resultstream;
QTimer timer;
};
#endif // HEADLESS_H

View File

@ -42,80 +42,80 @@ const char *getLayerOptionText(int mode, int disp)
{
case LOPT_BIOMES:
switch (disp) {
case 0: return QApplication::translate("LayerDialog", "1:1").toUtf8().data();
case 1: return QApplication::translate("LayerDialog", "1:4").toUtf8().data();
case 2: return QApplication::translate("LayerDialog", "1:16").toUtf8().data();
case 3: return QApplication::translate("LayerDialog", "1:64").toUtf8().data();
case 4: return QApplication::translate("LayerDialog", "1:256").toUtf8().data();
case 0: return "1:1";
case 1: return "1:4";
case 2: return "1:16";
case 3: return "1:64";
case 4: return "1:256";
default: return nullptr;
}
case LOPT_NOISE_T_4:
switch (disp) {
case 0: return QApplication::translate("LayerDialog", "All").toUtf8().data();
case 1: return QApplication::translate("LayerDialog", "+A[0] 1:4096 x0.952381").toUtf8().data();
case 2: return QApplication::translate("LayerDialog", "+B[0] 1:4023 x0.952381").toUtf8().data();
case 3: return QApplication::translate("LayerDialog", "+A[1] 1:1024 x0.158730").toUtf8().data();
case 4: return QApplication::translate("LayerDialog", "+B[1] 1:1005 x0.158730").toUtf8().data();
case 0: return "All";
case 1: return "+A[0] 1:4096 x0.952381";
case 2: return "+B[0] 1:4023 x0.952381";
case 3: return "+A[1] 1:1024 x0.158730";
case 4: return "+B[1] 1:1005 x0.158730";
default: return nullptr;
}
case LOPT_NOISE_H_4:
switch (disp) {
case 0: return QApplication::translate("LayerDialog", "All").toUtf8().data();
case 1: return QApplication::translate("LayerDialog", "+A[0] 1:1024 x0.564374").toUtf8().data();
case 2: return QApplication::translate("LayerDialog", "+B[0] 1:1005 x0.564374").toUtf8().data();
case 3: return QApplication::translate("LayerDialog", "+A[1] 1:512 x0.282187").toUtf8().data();
case 4: return QApplication::translate("LayerDialog", "+B[1] 1:502 x0.282187").toUtf8().data();
case 0: return "All";
case 1: return "+A[0] 1:1024 x0.564374";
case 2: return "+B[0] 1:1005 x0.564374";
case 3: return "+A[1] 1:512 x0.282187";
case 4: return "+B[1] 1:502 x0.282187";
default: return nullptr;
}
case LOPT_NOISE_C_4:
switch (disp) {
case 0: return QApplication::translate("LayerDialog", "All").toUtf8().data();
case 1: return QApplication::translate("LayerDialog", "+A[0] 1:2048 x0.751468").toUtf8().data();
case 2: return QApplication::translate("LayerDialog", "+B[0] 1:2011 x0.751468").toUtf8().data();
case 3: return QApplication::translate("LayerDialog", "+A[1] 1:1024 x0.375734").toUtf8().data();
case 4: return QApplication::translate("LayerDialog", "+B[1] 1:1005 x0.375734").toUtf8().data();
case 5: return QApplication::translate("LayerDialog", "+A[2] 1:512 x0.375734").toUtf8().data();
case 6: return QApplication::translate("LayerDialog", "+B[2] 1:502 x0.375734").toUtf8().data();
case 7: return QApplication::translate("LayerDialog", "+A[3] 1:256 x0.187867").toUtf8().data();
case 8: return QApplication::translate("LayerDialog", "+B[3] 1:251 x0.187867").toUtf8().data();
case 9: return QApplication::translate("LayerDialog", "+A[4] 1:128 x0.093933").toUtf8().data();
case 10: return QApplication::translate("LayerDialog", "+B[4] 1:125 x0.093933").toUtf8().data();
case 11: return QApplication::translate("LayerDialog", "+A[5] 1:64 x0.023483").toUtf8().data();
case 12: return QApplication::translate("LayerDialog", "+B[5] 1:62 x0.023483").toUtf8().data();
case 13: return QApplication::translate("LayerDialog", "+A[6] 1:32 x0.011742").toUtf8().data();
case 14: return QApplication::translate("LayerDialog", "+B[6] 1:31 x0.011742").toUtf8().data();
case 0: return "All";
case 1: return "+A[0] 1:2048 x0.751468";
case 2: return "+B[0] 1:2011 x0.751468";
case 3: return "+A[1] 1:1024 x0.375734";
case 4: return "+B[1] 1:1005 x0.375734";
case 5: return "+A[2] 1:512 x0.375734";
case 6: return "+B[2] 1:502 x0.375734";
case 7: return "+A[3] 1:256 x0.187867";
case 8: return "+B[3] 1:251 x0.187867";
case 9: return "+A[4] 1:128 x0.093933";
case 10: return "+B[4] 1:125 x0.093933";
case 11: return "+A[5] 1:64 x0.023483";
case 12: return "+B[5] 1:62 x0.023483";
case 13: return "+A[6] 1:32 x0.011742";
case 14: return "+B[6] 1:31 x0.011742";
default: return nullptr;
}
case LOPT_NOISE_E_4:
switch (disp) {
case 0: return QApplication::translate("LayerDialog", "All").toUtf8().data();
case 1: return QApplication::translate("LayerDialog", "+A[0] 1:2048 x0.716846").toUtf8().data();
case 2: return QApplication::translate("LayerDialog", "+B[0] 1:2011 x0.716846").toUtf8().data();
case 3: return QApplication::translate("LayerDialog", "+A[1] 1:1024 x0.358423").toUtf8().data();
case 4: return QApplication::translate("LayerDialog", "+B[1] 1:1005 x0.358423").toUtf8().data();
case 5: return QApplication::translate("LayerDialog", "+A[2] 1:256 x0.089606").toUtf8().data();
case 6: return QApplication::translate("LayerDialog", "+B[2] 1:251 x0.089606").toUtf8().data();
case 7: return QApplication::translate("LayerDialog", "+A[3] 1:128 x0.044803").toUtf8().data();
case 8: return QApplication::translate("LayerDialog", "+B[3] 1:125 x0.044803").toUtf8().data();
case 0: return "All";
case 1: return "+A[0] 1:2048 x0.716846";
case 2: return "+B[0] 1:2011 x0.716846";
case 3: return "+A[1] 1:1024 x0.358423";
case 4: return "+B[1] 1:1005 x0.358423";
case 5: return "+A[2] 1:256 x0.089606";
case 6: return "+B[2] 1:251 x0.089606";
case 7: return "+A[3] 1:128 x0.044803";
case 8: return "+B[3] 1:125 x0.044803";
default: return nullptr;
}
case LOPT_NOISE_W_4:
switch (disp) {
case 0: return QApplication::translate("LayerDialog", "All").toUtf8().data();
case 1: return QApplication::translate("LayerDialog", "+A[0] 1:512 x0.634921").toUtf8().data();
case 2: return QApplication::translate("LayerDialog", "+B[0] 1:502 x0.634921").toUtf8().data();
case 3: return QApplication::translate("LayerDialog", "+A[1] 1:256 x0.634921").toUtf8().data();
case 4: return QApplication::translate("LayerDialog", "+B[1] 1:251 x0.634921").toUtf8().data();
case 5: return QApplication::translate("LayerDialog", "+A[2] 1:128 x0.158730").toUtf8().data();
case 6: return QApplication::translate("LayerDialog", "+B[2] 1:125 x0.158730").toUtf8().data();
case 0: return "All";
case 1: return "+A[0] 1:512 x0.634921";
case 2: return "+B[0] 1:502 x0.634921";
case 3: return "+A[1] 1:256 x0.634921";
case 4: return "+B[1] 1:251 x0.634921";
case 5: return "+A[2] 1:128 x0.158730";
case 6: return "+B[2] 1:125 x0.158730";
default: return nullptr;
}
case LOPT_HEIGHT_4:
switch (disp) {
case 0: return QApplication::translate("LayerDialog", "Grayscale").toUtf8().data();
case 1: return QApplication::translate("LayerDialog", "Shaded biome map").toUtf8().data();
case 2: return QApplication::translate("LayerDialog", "Contours on biomes").toUtf8().data();
case 3: return QApplication::translate("LayerDialog", "Shaded with contours").toUtf8().data();
case 0: return QT_TRANSLATE_NOOP("LayerDialog", "Grayscale");
case 1: return QT_TRANSLATE_NOOP("LayerDialog", "Shaded biome map");
case 2: return QT_TRANSLATE_NOOP("LayerDialog", "Contours on biomes");
case 3: return QT_TRANSLATE_NOOP("LayerDialog", "Shaded with contours");
default: return nullptr;
}
default:
@ -163,7 +163,7 @@ LayerDialog::LayerDialog(QWidget *parent, int mc)
const char *item = getLayerOptionText(i, j);
if (!item)
break;
QString s = QString::asprintf("%-24s", item);
QString s = QApplication::translate("LayerDialog", item).leftJustified(24);
if (j < 9)
s += "\tALT+"+QString::number(j+1);
items.append(s);
@ -171,13 +171,15 @@ LayerDialog::LayerDialog(QWidget *parent, int mc)
combo[i]->addItems(items);
}
QFont fmono;
fmono.setFamily(QString::fromUtf8("Monospace"));
for (int i = 0; i < LOPT_MAX; i++)
{
if (!radio[i])
continue;
connect(radio[i], &QRadioButton::toggled, this, &LayerDialog::onRadioChange);
if (combo[i])
combo[i]->setFont(*gp_font_mono);
combo[i]->setFont(fmono);
if (i >= LOPT_NOISE_T_4 && i <= LOPT_NOISE_W_4)
{
radio[i]->setEnabled(mc > MC_1_17);

View File

@ -20,7 +20,7 @@
</property>
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<family>Monospace</family>
</font>
</property>
</widget>

View File

@ -1,11 +1,14 @@
#include "mainwindow.h"
#include "headless.h"
#include "aboutdialog.h"
#include "world.h"
#include <QApplication>
#include <QFontDatabase>
#include <QStandardPaths>
#include <QDir>
#include "world.h"
#include "cubiomes/generator.h"
#include "cubiomes/util.h"
@ -16,9 +19,6 @@ unsigned char g_tempsColors[256][3];
ExtGenConfig g_extgen;
QFont *gp_font_default;
QFont *gp_font_mono;
extern "C"
int getStructureConfig_override(int stype, int mc, StructureConfig *sconf)
{
@ -33,53 +33,97 @@ int getStructureConfig_override(int stype, int mc, StructureConfig *sconf)
}
return ok;
}
#include "QDebug"
int main(int argc, char *argv[])
{
initBiomes();
initBiomeColors(g_biomeColors);
initBiomeTypeColors(g_tempsColors);
QApplication app(argc, argv);
QCoreApplication::setApplicationName(APP_STRING);
bool version = false;
bool nogui = false;
bool reset = false;
bool usage = false;
QString sessionpath;
QString resultspath;
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "--reset-all") == 0)
{
QSettings settings(APP_STRING, APP_STRING);
settings.clear();
if (strcmp(argv[i], "--version") == 0)
version = true;
else if (strcmp(argv[i], "--nogui") == 0)
nogui = true;
else if (strcmp(argv[i], "--reset-all") == 0)
reset = true;
else if (strncmp(argv[i], "--session=", 10) == 0)
sessionpath = argv[i] + 10;
else if (strncmp(argv[i], "--session", 9) == 0 && i+1 < argc)
sessionpath = argv[++i];
else if (strncmp(argv[i], "--out=", 6) == 0)
resultspath = argv[i] + 6;
else if (strncmp(argv[i], "--out", 5) == 0 && i+1 < argc)
resultspath = argv[++i];
else
usage = true;
}
QString path = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
QDir dir(path);
if (dir.exists() && path.contains(APP_STRING))
{
dir.removeRecursively();
}
if (usage)
{
const char *msg =
"Usage: cubiomes-viewer [options]\n"
"Options:\n"
" --help Display this help and exit.\n"
" --version Output version information and exit.\n"
" --nogui Run in headless search mode.\n"
" --reset-all Clear settings and remove all session data.\n"
" --session=file Open this session file.\n"
" --out=file Write matching seeds to this file while searching.\n"
"\n";
printf("%s", msg);
exit(0);
}
if (version)
{
printf("%s %s\n", APP_STRING, getVersStr().toLocal8Bit().data());
exit(0);
}
if (reset)
{
QSettings settings(APP_STRING, APP_STRING);
settings.clear();
QString path = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
QDir dir(path);
if (dir.exists() && path.contains(APP_STRING))
{
dir.removeRecursively();
}
}
//int fontid = QFontDatabase::addApplicationFont(":/fonts/test.ttf");
int fontid = QFontDatabase::addApplicationFont(":/fonts/DejaVuSans.ttf");
if (fontid >= 0)
if (sessionpath.isEmpty())
{
QFontDatabase::addApplicationFont(":/fonts/DejaVuSans-Bold.ttf");
int fontid_mono = QFontDatabase::addApplicationFont(":/fonts/DejaVuSansMono.ttf");
static QFont font_default = QFontDatabase::applicationFontFamilies(fontid).at(0);
font_default.setPointSize(10);
static QFont font_mono = QFontDatabase::applicationFontFamilies(fontid_mono).at(0);
font_mono.setPointSize(9);
app.setFont(font_default);
gp_font_default = &font_default;
gp_font_mono = &font_mono;
QString path = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
QDir dir(path);
if (!dir.exists())
dir.mkpath(".");
sessionpath = path + "/session.save";
}
MainWindow mw;
mw.show();
int ret = app.exec();
return ret;
if (nogui)
{
QCoreApplication app(argc, argv);
Headless(sessionpath, resultspath);
return app.exec();
}
else
{
QApplication app(argc, argv);
MainWindow mw(sessionpath, resultspath);
mw.show();
return app.exec();
}
}

View File

@ -12,6 +12,7 @@
#include "tabtriggers.h"
#include "tabbiomes.h"
#include "tabstructures.h"
#include "message.h"
#if WITH_UPDATER
#include "updater.h"
@ -37,7 +38,7 @@
#include <QLibraryInfo>
MainWindow::MainWindow(QWidget *parent)
MainWindow::MainWindow(QString sessionpath, QString resultspath, QWidget *parent)
: QMainWindow(parent)
, ui()
, dock()
@ -48,6 +49,7 @@ MainWindow::MainWindow(QWidget *parent)
, lopt()
, config()
, mconfig(false)
, sessionpath(sessionpath)
, prevdir(".")
, autosaveTimer()
, prevtab(-1)
@ -57,7 +59,10 @@ MainWindow::MainWindow(QWidget *parent)
QSettings settings(APP_STRING, APP_STRING);
QString lang = settings.value("config/lang", QLocale().name()).toString();
if (!loadTranslation(lang))
{
loadTranslation("en_US");
settings.setValue("config/lang", "en_US");
}
ui = new Ui::MainWindow;
dock = new QDockWidget(tr("Map"), this);
@ -127,9 +132,9 @@ MainWindow::MainWindow(QWidget *parent)
ui->toolBar->addAction(toorigin);
ui->toolBar->addSeparator();
dimactions[0] = addMapAction(-1, "overworld", tr("Overworld"));
dimactions[1] = addMapAction(-1, "nether", tr("Nether"));
dimactions[2] = addMapAction(-1, "the_end", tr("End"));
dimactions[0] = addMapAction(":/icons/overworld", tr("Overworld"));
dimactions[1] = addMapAction(":/icons/nether", tr("Nether"));
dimactions[2] = addMapAction(":/icons/the_end", tr("End"));
dimgroup = new QActionGroup(this);
for (int i = 0; i < 3; i++)
@ -141,33 +146,33 @@ MainWindow::MainWindow(QWidget *parent)
dimactions[0]->setChecked(true);
saction.resize(D_STRUCT_NUM);
addMapAction(D_GRID, "grid", tr("Show grid"));
addMapAction(D_SLIME, "slime", tr("Show slime chunks"));
addMapAction(D_SPAWN, "spawn", tr("Show world spawn"));
addMapAction(D_STRONGHOLD, "stronghold", tr("Show strongholds"));
addMapAction(D_VILLAGE, "village", tr("Show villages"));
addMapAction(D_MINESHAFT, "mineshaft", tr("Show abandoned mineshafts"));
addMapAction(D_DESERT, "desert", tr("Show desert pyramids"));
addMapAction(D_JUNGLE, "jungle", tr("Show jungle temples"));
addMapAction(D_HUT, "hut", tr("Show swamp huts"));
addMapAction(D_MONUMENT, "monument", tr("Show ocean monuments"));
addMapAction(D_IGLOO, "igloo", tr("Show igloos"));
addMapAction(D_MANSION, "mansion", tr("Show woodland mansions"));
addMapAction(D_RUINS, "ruins", tr("Show ocean ruins"));
addMapAction(D_SHIPWRECK, "shipwreck", tr("Show shipwrecks"));
addMapAction(D_TREASURE, "treasure", tr("Show buried treasures"));
addMapAction(D_WELL, "well", tr("Show desert wells"));
addMapAction(D_GEODE, "geode", tr("Show amethyst geodes"));
addMapAction(D_OUTPOST, "outpost", tr("Show pillager outposts"));
addMapAction(D_PORTAL, "portal", tr("Show ruined portals"));
addMapAction(D_ANCIENTCITY, "ancient_city", tr("Show ancient cities"));
addMapAction(D_TRAIL, "trail", tr("Show trail ruins"));
addMapAction(D_GRID);
addMapAction(D_SLIME);
addMapAction(D_SPAWN);
addMapAction(D_STRONGHOLD);
addMapAction(D_VILLAGE);
addMapAction(D_MINESHAFT);
addMapAction(D_DESERT);
addMapAction(D_JUNGLE);
addMapAction(D_HUT);
addMapAction(D_MONUMENT);
addMapAction(D_IGLOO);
addMapAction(D_MANSION);
addMapAction(D_RUINS);
addMapAction(D_SHIPWRECK);
addMapAction(D_TREASURE);
addMapAction(D_WELL);
addMapAction(D_GEODE);
addMapAction(D_OUTPOST);
addMapAction(D_PORTAL);
addMapAction(D_ANCIENTCITY);
addMapAction(D_TRAILS);
ui->toolBar->addSeparator();
addMapAction(D_FORTESS, "fortress", tr("Show nether fortresses"));
addMapAction(D_BASTION, "bastion", tr("Show bastions"));
addMapAction(D_FORTESS);
addMapAction(D_BASTION);
ui->toolBar->addSeparator();
addMapAction(D_ENDCITY, "endcity", tr("Show end cities"));
addMapAction(D_GATEWAY, "gateway", tr("Show end gateways"));
addMapAction(D_ENDCITY);
addMapAction(D_GATEWAY);
saction[D_GRID]->setChecked(true);
@ -246,6 +251,8 @@ MainWindow::MainWindow(QWidget *parent)
ui->collapseControl->init(tr("Matching seeds"), formControl, false);
connect(formControl, &FormSearchControl::selectedSeedChanged, this, &MainWindow::onSelectedSeedChanged);
connect(formControl, &FormSearchControl::searchStatusChanged, this, &MainWindow::onSearchStatusChanged);
if (!resultspath.isEmpty())
formControl->setResultsPath(resultspath);
ui->collapseControl->setInfo(
tr("Help: Matching seeds"),
tr(
@ -299,31 +306,34 @@ bool MainWindow::loadTranslation(QString lang)
return true;
}
QAction *MainWindow::addMapAction(int sopt, const char *iconpath, QString tip)
QAction *MainWindow::addMapAction(int opt)
{
QIcon icon;
QString inam = QString(":icons/") + iconpath;
QPixmap pix_on = QPixmap(inam + ".png");
QPixmap pix_off = QPixmap(inam + "_d.png");
QString rc = QString(":/icons/") + mapopt2str(opt);
QString tip = tr("Show %1").arg(mapopt2display(opt));
QAction *action = addMapAction(rc, tip);
action->connect(action, &QAction::toggled, [=](bool state){ this->onActionMapToggled(opt, state); });
saction[opt] = action;
return action;
}
QAction *MainWindow::addMapAction(QString rcbase, QString tip)
{
QPixmap pix_on = QPixmap(rcbase + ".png");
QPixmap pix_off = QPixmap(rcbase + "_d.png");
if (pix_on.size().width() > 20)
{
pix_on = pix_on.scaledToWidth(20);
pix_off = pix_off.scaledToWidth(20);
}
QIcon icon;
icon.addPixmap(pix_on, QIcon::Normal, QIcon::On);
icon.addPixmap(pix_off, QIcon::Normal, QIcon::Off);
QAction *action = new QAction(icon, tip, this);
action->setCheckable(true);
ui->toolBar->addAction(action);
if (sopt >= 0)
{
action->connect(action, &QAction::toggled, [=](bool state){ this->onActionMapToggled(sopt, state); });
saction[sopt] = action;
}
return action;
}
MapView* MainWindow::getMapView()
{
return mapView;
@ -375,29 +385,24 @@ bool MainWindow::setSeed(WorldInfo wi, int dim)
else
dim = getDim();
MapView *mapview = getMapView();
uint64_t current = mapview->world ? mapview->world->wi.seed : wi.seed;
if (current != wi.seed)
const QList<QAction*> hist = ui->menuHistory->actions();
if (hist.empty() || hist.front()->data().toULongLong() != wi.seed)
{
QList<QAction*> hist = ui->menuHistory->actions();
bool rm = false;
for (QAction *act : qAsConst(hist))
QAction *rm = nullptr;
if (hist.size() >= 16)
rm = hist.back();
for (QAction *act : hist)
if (act->data().toULongLong() == wi.seed)
rm = act;
if (rm)
{
if (act->data().toULongLong() == current)
{
ui->menuHistory->removeAction(act);
hist.back()->deleteLater();
rm = true;
}
ui->menuHistory->removeAction(rm);
rm->deleteLater();
}
if (!rm && hist.size() >= 12)
{
ui->menuHistory->removeAction(hist.back());
hist.back()->deleteLater();
}
QString s = QString::asprintf("%" PRId64, current);
QString s = QString::asprintf("%" PRId64, wi.seed);
QAction *act = new QAction(s, this);
act->setData(QVariant::fromValue(current));
act->setData(QVariant::fromValue(wi.seed));
act->connect(act, &QAction::triggered, [=](){ this->onActionHistory(act); });
ui->menuHistory->insertAction(hist.empty() ? 0 : hist.first(), act);
ui->menuHistory->setEnabled(true);
@ -476,11 +481,7 @@ void MainWindow::saveSettings()
if (config.restoreSession)
{
QString path = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
QDir dir(path);
if (!dir.exists())
dir.mkpath(".");
saveProgress(path + "/session.save", true);
saveSession(sessionpath, true);
}
}
@ -535,176 +536,52 @@ void MainWindow::loadSettings()
onUpdateConfig();
onUpdateMapConfig();
if (config.restoreSession)
if (config.restoreSession && QFile::exists(sessionpath))
{
QString path = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
path += "/session.save";
if (QFile::exists(path))
{
loadProgress(path, false, false);
}
loadSession(sessionpath, false, false);
}
}
bool MainWindow::saveProgress(QString fnam, bool quiet)
bool MainWindow::saveSession(QString fnam, bool quiet)
{
QFile file(fnam);
Session session;
session.sc = formControl->getSearchConfig();
session.gen48 = formGen48->getConfig(false);
session.cv = formCond->getConditions();
session.slist = formControl->getResults();
getSeed(&session.wi);
if (!file.open(QIODevice::WriteOnly))
{
if (!quiet)
warning(tr("Failed to open file:\n\"%1\"").arg(fnam));
return false;
}
SearchConfig searchconf = formControl->getSearchConfig();
Gen48Config gen48 = formGen48->getConfig(false);
QVector<Condition> condvec = formCond->getConditions();
QVector<uint64_t> results = formControl->getResults();
WorldInfo wi;
getSeed(&wi);
QTextStream stream(&file);
stream << "#Version: " << VERS_MAJOR << "." << VERS_MINOR << "." << VERS_PATCH << "\n";
stream << "#Time: " << QDateTime::currentDateTime().toString() << "\n";
// MC version of the session should take priority over the one in the settings
wi.write(stream);
searchconf.write(stream);
gen48.write(stream);
for (Condition &c : condvec)
stream << "#Cond: " << c.toHex() << "\n";
for (uint64_t s : qAsConst(results))
stream << QString::asprintf("%" PRId64 "\n", (int64_t)s);
return true;
return session.save(this, fnam, quiet);
}
bool MainWindow::loadProgress(QString fnam, bool keepresults, bool quiet)
bool MainWindow::loadSession(QString fnam, bool keepresults, bool quiet)
{
QFile file(fnam);
Session session;
// build current session before loading to keep unspecified values the same
getSeed(&session.wi);
session.sc = formControl->getSearchConfig();
session.gen48 = formGen48->getConfig(false);
if (!file.open(QIODevice::ReadOnly))
{
if (!quiet)
warning(tr("Failed to open progress file:\n\"%1\"").arg(fnam));
if (!session.load(this, fnam, quiet))
return false;
}
int major = 0, minor = 0, patch = 0;
SearchConfig searchconf = formControl->getSearchConfig();
Gen48Config gen48 = formGen48->getConfig(false);
QVector<Condition> condvec;
QVector<uint64_t> seeds;
WorldInfo wi;
getSeed(&wi, true);
QTextStream stream(&file);
QString line;
line = stream.readLine();
int lno = 1;
if (sscanf(line.toLocal8Bit().data(), "#Version: %d.%d.%d", &major, &minor, &patch) != 3)
{
if (quiet)
return false;
int button = QMessageBox::warning(this, tr("Warning"),
tr("File does not look like a progress file.\n"
"Progress may be incomplete or broken.\n\n"
"Continue anyway?"),
QMessageBox::Abort|QMessageBox::Yes);
if (button == QMessageBox::Abort)
return false;
}
else if (cmpVers(major, minor, patch) > 0)
{
if (quiet)
return false;
int button = QMessageBox::warning(this, tr("Warning"),
tr("File was created with a newer version.\n"
"Progress may be incomplete or broken.\n\n"
"Continue loading progress anyway?"),
QMessageBox::Abort|QMessageBox::Yes);
if (button == QMessageBox::Abort)
return false;
}
while (stream.status() == QTextStream::Ok && !stream.atEnd())
{
lno++;
line = stream.readLine();
if (line.isEmpty()) continue;
if (line.startsWith("#Time:")) continue;
if (line.startsWith("#Title:")) continue;
if (line.startsWith("#Desc:")) continue;
if (searchconf.read(line)) continue;
if (gen48.read(line)) continue;
if (wi.read(line)) continue;
if (line.startsWith("#Cond:"))
{ // Conditions
Condition c;
if (c.readHex(line.mid(6).trimmed()))
{
condvec.push_back(c);
}
else
{
if (quiet)
return false;
int button = QMessageBox::warning(this, tr("Warning"),
tr("Condition [%1] at line %2 is not supported.\n\n"
"Continue anyway?").arg(c.save).arg(lno),
QMessageBox::Abort|QMessageBox::Yes);
if (button == QMessageBox::Abort)
return false;
}
}
else
{ // Seeds
QByteArray ba = line.toLocal8Bit();
const char *p = ba.data();
uint64_t s;
if (sscanf(p, "%" PRId64, (int64_t*)&s) == 1)
{
seeds.push_back(s);
}
else
{
if (quiet)
return false;
int button = QMessageBox::warning(this, tr("Warning"),
tr("Failed to parse line %1 of progress file:\n%2\n\n"
"Continue anyway?").arg(lno).arg(line),
QMessageBox::Abort|QMessageBox::Yes);
if (button == QMessageBox::Abort)
return false;
}
}
}
setSeed(wi);
setSeed(session.wi);
formCond->on_buttonRemoveAll_clicked();
for (Condition &c : condvec)
for (Condition &c : session.cv)
{
QListWidgetItem *item = new QListWidgetItem();
formCond->addItemCondition(item, c);
}
formGen48->setConfig(gen48, quiet);
formGen48->setConfig(session.gen48, quiet);
formGen48->updateCount();
if (!keepresults)
formControl->on_buttonClear_clicked();
formControl->setSearchConfig(searchconf, quiet);
formControl->searchResultsAdd(seeds, false);
formControl->setSearchConfig(session.sc, quiet);
formControl->searchResultsAdd(session.slist, false);
formControl->searchProgressReset();
return true;
@ -760,27 +637,6 @@ void MainWindow::setDockable(bool dockable)
}
}
void MainWindow::applyConfigChanges(const Config old, const Config conf)
{
this->config = conf;
getMapView()->setConfig(conf);
if (old.uistyle != conf.uistyle)
onStyleChanged(conf.uistyle);
if (conf.autosaveCycle)
{
autosaveTimer.setInterval(conf.autosaveCycle * 60000);
autosaveTimer.start();
}
else
{
autosaveTimer.stop();
}
if (!conf.biomeColorPath.isEmpty() || !old.biomeColorPath.isEmpty())
onBiomeColorChange();
}
void MainWindow::setMCList(bool experimental)
{
WorldInfo wi;
@ -808,11 +664,6 @@ void MainWindow::setMCList(bool experimental)
ui->comboBoxMC->setEnabled(true);
}
int MainWindow::warning(QString text, QMessageBox::StandardButtons buttons)
{
return QMessageBox::warning(this, tr("Warning"), text, buttons);
}
void MainWindow::mapGoto(qreal x, qreal z, qreal scale)
{
getMapView()->setView(x, z, scale);
@ -867,7 +718,7 @@ void MainWindow::on_actionSave_triggered()
{
QFileInfo finfo(fnam);
prevdir = finfo.absolutePath();
saveProgress(fnam);
saveSession(fnam);
}
}
@ -879,7 +730,7 @@ void MainWindow::on_actionLoad_triggered()
{
QFileInfo finfo(fnam);
prevdir = finfo.absolutePath();
loadProgress(fnam, false, false);
loadSession(fnam, false, false);
}
}
@ -934,7 +785,7 @@ void MainWindow::on_actionPresetLoad_triggered()
PresetDialog *dialog = new PresetDialog(this, wi, false);
dialog->setActiveFilter(formCond->getConditions());
if (dialog->exec() && !dialog->rc.isEmpty())
loadProgress(dialog->rc, true, false);
loadSession(dialog->rc, true, false);
}
void MainWindow::on_actionExamples_triggered()
@ -944,7 +795,7 @@ void MainWindow::on_actionExamples_triggered()
PresetDialog *dialog = new PresetDialog(this, wi, true);
dialog->setActiveFilter(formCond->getConditions());
if (dialog->exec() && !dialog->rc.isEmpty())
loadProgress(dialog->rc, true, false);
loadSession(dialog->rc, true, false);
}
void MainWindow::on_actionAbout_triggered()
@ -965,8 +816,8 @@ void MainWindow::on_actionPaste_triggered()
void MainWindow::on_actionAddShadow_triggered()
{
const QVector<uint64_t> results = formControl->getResults();
QVector<uint64_t> shadows;
const std::vector<uint64_t> results = formControl->getResults();
std::vector<uint64_t> shadows;
shadows.reserve(results.size());
for (uint64_t s : results)
shadows.push_back( getShadow(s) );
@ -1059,7 +910,7 @@ void MainWindow::onAutosaveTimeout()
if (config.autosaveCycle)
{
QString path = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
saveProgress(path + "/session.save", true);
saveSession(path + "/session.save", true);
//int dispms = 10000;
//if (saveProgress(path + "/session.save", true))
// ui->statusBar->showMessage(tr("Session autosaved"), dispms);
@ -1072,8 +923,8 @@ void MainWindow::onActionHistory(QAction *act)
{
uint64_t seed = act->data().toULongLong();
onSelectedSeedChanged(seed);
ui->menuHistory->removeAction(act);
act->deleteLater();
//ui->menuHistory->removeAction(act);
//act->deleteLater();
}
void MainWindow::onActionMapToggled(int sopt, bool show)
@ -1129,8 +980,7 @@ void MainWindow::onUpdateConfig()
if (old.lang != config.lang)
{
QString msg = tr("The application will need to be restarted before all changes can take effect.");
QMessageBox::information(this, tr("Restart required"), msg);
info(this, tr("The application will need to be restarted before all changes can take effect."));
}
getMapView()->setConfig(config);
@ -1139,6 +989,38 @@ void MainWindow::onUpdateConfig()
if (!old.biomeColorPath.isEmpty() || !config.biomeColorPath.isEmpty())
onBiomeColorChange();
if (old.fontNorm != config.fontNorm || old.fontMono != config.fontMono)
{
QFont fnorm = config.fontNorm;
QFont fmono = config.fontMono;
QFont fbold;
if (fnorm.family() == "Monospace") // avoid identification conflict
fnorm = QApplication::font();
fnorm.setStyleHint(QFont::AnyStyle);
fmono.setStyleHint(QFont::Monospace);
fmono.setFixedPitch(true);
fbold = fnorm;
fbold.setBold(true);
QApplication::setFont(fnorm);
QWidgetList wlist = QApplication::allWidgets();
for (QWidget *w : qAsConst(wlist))
{
const QFont& f = w->font();
if (f.styleHint() == QFont::Monospace || f.family() == "Monospace")
w->setFont(fmono);
else if (f.bold())
w->setFont(fbold);
else
w->setFont(fnorm);
}
// update cascade
for (QWidget *w : qAsConst(wlist))
w->update();
}
if (config.autosaveCycle)
{
autosaveTimer.setInterval(config.autosaveCycle * 60000);

View File

@ -30,7 +30,6 @@ namespace Ui {
class MainWindow;
}
struct ISaveTab
{
virtual void save(QSettings& settings) = 0;
@ -45,13 +44,14 @@ class MainWindow : public QMainWindow
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
explicit MainWindow(QString sessionpath, QString resultspath, QWidget *parent = 0);
virtual ~MainWindow();
virtual void closeEvent(QCloseEvent *event) override;
bool loadTranslation(QString lang);
QAction *addMapAction(int sopt, const char *iconpath, QString tip);
QAction *addMapAction(int opt);
QAction *addMapAction(QString rcbase, QString tip);
bool getSeed(WorldInfo *wi, bool applyrand = true);
bool setSeed(WorldInfo wi, int dim = DIM_UNDEF);
@ -61,18 +61,16 @@ public:
protected:
void saveSettings();
void loadSettings();
bool saveProgress(QString fnam, bool quiet = false);
bool loadProgress(QString fnam, bool keepresults, bool quiet);
bool saveSession(QString fnam, bool quiet = false);
bool loadSession(QString fnam, bool keepresults, bool quiet);
void updateMapSeed();
void setDockable(bool dockable);
void applyConfigChanges(const Config old, const Config conf);
void setMCList(bool experimental);
signals:
void mapUpdated();
public slots:
int warning(QString text, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
void mapGoto(qreal x, qreal z, qreal scale);
void setBiomeColorRc(QString rc);
@ -134,6 +132,7 @@ public:
LayerOpt lopt;
Config config;
MapConfig mconfig;
QString sessionpath;
QString prevdir;
QTimer autosaveTimer;
int prevtab;

View File

@ -54,8 +54,9 @@ MapView::MapView(QWidget *parent)
{
memset(sshow, 0, sizeof(sshow));
QFont mono = *gp_font_mono;
setFont(mono);
QFont fmono;
fmono.setFamily(QString::fromUtf8("Monospace"));
setFont(fmono);
QPalette pal = palette();
pal.setColor(QPalette::Background, Qt::black);
@ -68,7 +69,7 @@ MapView::MapView(QWidget *parent)
overlay = new MapOverlay(this);
overlay->setMouseTracking(true);
overlay->setFont(mono);
overlay->setFont(font());
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
@ -377,27 +378,35 @@ void MapView::showContextMenu(const QPoint &pos)
cpy_dat.push_back({ tr("Copy chunk:"), QString::asprintf("%d %d", vp.p.x >> 4, vp.p.z >> 4) });
cpy_dat.push_back({ tr("Copy region:"), QString::asprintf("%d %d", vp.p.x >> 9, vp.p.z >> 9) });
int pad = 0;
int wmax = 0;
for (auto& it : cpy_dat)
{
if (it.txt.length() > pad)
pad = it.txt.length();
int w = menu.fontMetrics().horizontalAdvance(it.txt + " ");
if (w > wmax)
wmax = w;
}
pad += 1;
menu.addAction(tr("Go to coordinates..."), this, &MapView::onGoto, QKeySequence(Qt::CTRL + Qt::Key_G));
if (world)
{
QString txt = tr("Copy seed:").leftJustified(pad) + QString::asprintf("%" PRId64, (int64_t)world->wi.seed);
QString txt = tr("Copy seed:");
while (menu.fontMetrics().horizontalAdvance(txt + " ") < wmax)
txt += " ";
txt += QString::asprintf("%" PRId64, (int64_t)world->wi.seed);
menu.addAction(txt, this, &MapView::copySeed, QKeySequence::Copy);
}
for (auto& it : cpy_dat)
{
QString txt = it.txt.leftJustified(pad) + it.cpy;
QString txt = it.txt;
while (menu.fontMetrics().horizontalAdvance(txt + " ") < wmax)
txt += " ";
txt += it.cpy;
menu.addAction(txt, [=](){ this->copyText(it.cpy); });
}
//menu.addAction(tr("Animation"), this, &MapView::runAni);
for (QAction *act : menu.actions())
act->setFont(font());
menu.exec(mapToGlobal(pos));
}
@ -616,6 +625,11 @@ void MapView::keyPressEvent(QKeyEvent *e)
{
if (e->matches(QKeySequence::Copy))
copySeed();
else if (e->matches(QKeySequence::ZoomIn) || e->key() == Qt::Key_Plus)
zoom(pow(2, +0.125));
else if (e->matches(QKeySequence::ZoomOut) || e->key() == Qt::Key_Minus)
zoom(pow(2, -0.125));
qreal step = 4 / blocks2pix;
switch (e->key())
{
@ -623,12 +637,6 @@ void MapView::keyPressEvent(QKeyEvent *e)
if (e->modifiers() == 0)
setView(0, 0);
break;
case Qt::Key_Plus:
zoom(pow(2, +0.125));
break;
case Qt::Key_Minus:
zoom(pow(2, -0.125));
break;
case Qt::Key_Up:
focusz -= step;
update();

81
src/message.cpp Normal file
View File

@ -0,0 +1,81 @@
#include "message.h"
#include <QApplication>
#include <QTextStream>
static
int term_prompt(const QString& title, const QString& text, QMessageBox::StandardButtons buttons)
{
QTextStream out (stdout);
out << QString("[%1]\n%2\n---\n").arg(title).arg(text);
std::vector<std::pair<QString, int>> opts;
if (buttons & QMessageBox::Ok)
opts.push_back(std::make_pair(QApplication::translate("QPlatformTheme", "OK"), QMessageBox::Ok));
if (buttons & QMessageBox::Yes)
opts.push_back(std::make_pair(QApplication::translate("QPlatformTheme", "Yes"), QMessageBox::Yes));
if (buttons & QMessageBox::No)
opts.push_back(std::make_pair(QApplication::translate("QPlatformTheme", "No"), QMessageBox::No));
if (buttons & QMessageBox::Abort)
opts.push_back(std::make_pair(QApplication::translate("QPlatformTheme", "Abort"), QMessageBox::Abort));
if (buttons & QMessageBox::Cancel)
opts.push_back(std::make_pair(QApplication::translate("QPlatformTheme", "Cancel"), QMessageBox::Cancel));
if (buttons & QMessageBox::Ignore)
opts.push_back(std::make_pair(QApplication::translate("QPlatformTheme", "Ignore"), QMessageBox::Ignore));
if (buttons & QMessageBox::Reset)
opts.push_back(std::make_pair(QApplication::translate("QPlatformTheme", "Reset"), QMessageBox::Reset));
if (buttons & QMessageBox::Save)
opts.push_back(std::make_pair(QApplication::translate("QPlatformTheme", "Save"), QMessageBox::Save));
if (buttons & QMessageBox::Discard)
opts.push_back(std::make_pair(QApplication::translate("QPlatformTheme", "Discard"), QMessageBox::Discard));
if (opts.size() <= 1)
return buttons;
for (size_t i = 0; i < opts.size(); i++)
out << QString("%1%2 [%3]").arg(i==0 ? "" : ", ").arg(opts[i].first).arg(i+1);
out << ":";
out.flush();
QTextStream in (stdin);
uint num = 0;
while (true)
{
num = in.readLine().toUInt();
if (num >= 1 && num <= opts.size())
break;
out << "Invalid option, try again: ";
out.flush();
}
return opts[num-1].second;
}
static
int message(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons)
{
if (!parent)
return term_prompt(title, text, buttons);
// emulate the behaviour of QMessageBox::warning(...), but raise as active window
QMessageBox w(icon, title, text, QMessageBox::NoButton, parent);
for (uint mask = QMessageBox::FirstButton; mask <= QMessageBox::LastButton; mask <<= 1)
if (mask & buttons)
w.addButton((QMessageBox::StandardButton)mask);
parent->setWindowState(Qt::WindowActive);
//w.setWindowState(Qt::WindowActive);
if (w.exec() == -1)
return QMessageBox::Cancel;
return w.standardButton(w.clickedButton());
}
int warn(QWidget *parent, const QString& title, const QString& text, QMessageBox::StandardButtons buttons)
{
return message(parent, QMessageBox::Warning, title, text, buttons);
}
int warn(QWidget *parent, const QString& text, QMessageBox::StandardButtons buttons)
{
return message(parent, QMessageBox::Warning, QApplication::tr("Warning"), text, buttons);
}
int info(QWidget *parent, const QString& text, QMessageBox::StandardButtons buttons)
{
return message(parent, QMessageBox::Information, QApplication::tr("Information"), text, buttons);
}

10
src/message.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef MESSAGE_H
#define MESSAGE_H
#include <QMessageBox>
int warn(QWidget *parent, const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
int warn(QWidget *parent, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
int info(QWidget *parent, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
#endif // MESSAGE_H

View File

@ -61,8 +61,6 @@ PresetDialog::PresetDialog(QWidget *parent, WorldInfo wi, bool showEamples)
connect(ui->buttonOk, &QPushButton::clicked, this, &QDialog::accept);
connect(ui->buttonCancel, &QPushButton::clicked, this, &QDialog::reject);
ui->listFilters->setFont(*gp_font_mono);
if (showEamples)
ui->tabWidget->setCurrentWidget(ui->tabExamples);
else

View File

@ -60,7 +60,6 @@
<property name="font">
<font>
<family>Monospace</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="alternatingRowColors">
@ -83,7 +82,6 @@
<property name="font">
<font>
<family>Monospace</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="alternatingRowColors">

View File

@ -12,12 +12,13 @@
#include <QApplication>
#include <QStandardPaths>
#include <QDir>
#include <QDebug>
#include <algorithm>
#define MULTIPLY_CHAR QChar(0xD7)
QString Condition::summary() const
QString Condition::summary(const QFont *font) const
{
const FilterInfo& ft = g_filterinfo.list[type];
QString s;
@ -32,7 +33,7 @@ QString Condition::summary() const
return s;
}
QString cnts = "";
QString cnts;
if (ft.count)
cnts += MULTIPLY_CHAR + QString::number(count);
if (skipref)
@ -57,8 +58,19 @@ QString Condition::summary() const
}
}
s += QString(" %2%3").arg(txts, -28, ' ').arg(cnts, -4, QChar(' '));
if (font)
{
s += " " + txts;
QFontMetrics fm(*font);
int w = fm.horizontalAdvance(QString("%1").arg("#", 30));
while (fm.horizontalAdvance(s + "\t") < w)
s += "\t";
}
else
{
s += QString(" %1").arg(txts, -25);
}
s += QString("\t%1").arg(cnts, -3);
if (relative)
s += QString::asprintf("[%02d]+", relative);
else
@ -273,7 +285,6 @@ void SearchThreadEnv::prepareSurfaceNoise(int dim)
}
}
#include <QDebug>
static
int testTreeAt(
Pos at, // relative origin
@ -573,67 +584,115 @@ int testTreeAt(
}
static const int g_qh_c_n = sizeof(low20QuadHutBarely) / sizeof(uint64_t);
static QuadInfo qh_constellations[g_qh_c_n];
// initialize global tables
void _init(void) __attribute__((constructor));
void _init(void)
static const QuadInfo *getQHInfo(uint64_t cst)
{
int st = Swamp_Hut;
StructureConfig sc;
getStructureConfig(st, MC_NEWEST, &sc);
sc.salt = 0; // ignore version dependent salt offsets
static std::map<uint64_t, QuadInfo> qh_info;
static QMutex mutex;
for (int i = 0; i < g_qh_c_n; i++)
mutex.lock();
if (qh_info.size() == 0)
{
uint64_t b = low20QuadHutBarely[i];
for (uint64_t s = b;; s += 0x100000)
StructureConfig sc;
getStructureConfig(Swamp_Hut, MC_NEWEST, &sc);
sc.salt = 0; // ignore version dependent salt offsets
for (size_t i = 0, n = sizeof(low20QuadHutBarely) / sizeof(uint64_t); i < n; i++)
{
QuadInfo *qi = &qh_constellations[i];
Pos pc;
if (scanForQuads(sc, 128, s, low20QuadHutBarely, g_qh_c_n,
20, 0, 0, 0, 1, 1, &pc, 1) < 1)
continue;
if ( !(qi->rad = isQuadBase(sc, s, 160)) )
continue;
uint64_t c = low20QuadHutBarely[i];
for (uint64_t s = c;; s += 0x100000)
{
// find a quad-hut for this constellation
Pos pc;
if (scanForQuads(sc, 128, s, low20QuadHutBarely, n, 20, 0, 0, 0, 1, 1, &pc, 1) < 1)
continue;
qreal rad = isQuadBase(sc, s, 160);
if (rad == 0)
continue;
qi->c = b;
qi->p[0] = getFeaturePos(sc, s, 0, 0);
qi->p[1] = getFeaturePos(sc, s, 0, 1);
qi->p[2] = getFeaturePos(sc, s, 1, 0);
qi->p[3] = getFeaturePos(sc, s, 1, 1);
qi->afk = getOptimalAfk(qi->p, 7,7,9, &qi->spcnt);
qi->typ = st;
QuadInfo *qi = &qh_info[c];
qi->rad = rad;
qi->c = c;
qi->p[0] = getFeaturePos(sc, s, 0, 0);
qi->p[1] = getFeaturePos(sc, s, 0, 1);
qi->p[2] = getFeaturePos(sc, s, 1, 0);
qi->p[3] = getFeaturePos(sc, s, 1, 1);
qi->afk = getOptimalAfk(qi->p, 7,7,9, &qi->spcnt);
qi->typ = Swamp_Hut;
qi->flt = F_QH_BARELY;
int j, n;
n = sizeof(low20QuadHutNormal) / sizeof(uint64_t);
for (j = 0; j < n; j++) {
if (low20QuadHutNormal[j] == b) {
qi->flt = F_QH_NORMAL;
break;
qi->flt = F_QH_BARELY;
int j, m;
m = sizeof(low20QuadHutNormal) / sizeof(uint64_t);
for (j = 0; j < m; j++) {
if (low20QuadHutNormal[j] == c) {
qi->flt = F_QH_NORMAL;
break;
}
}
}
n = sizeof(low20QuadClassic) / sizeof(uint64_t);
for (j = 0; j < n; j++) {
if (low20QuadClassic[j] == b) {
qi->flt = F_QH_CLASSIC;
break;
m = sizeof(low20QuadClassic) / sizeof(uint64_t);
for (j = 0; j < m; j++) {
if (low20QuadClassic[j] == c) {
qi->flt = F_QH_CLASSIC;
break;
}
}
}
n = sizeof(low20QuadIdeal) / sizeof(uint64_t);
for (j = 0; j < n; j++) {
if (low20QuadIdeal[j] == b) {
qi->flt = F_QH_IDEAL;
break;
m = sizeof(low20QuadIdeal) / sizeof(uint64_t);
for (j = 0; j < m; j++) {
if (low20QuadIdeal[j] == c) {
qi->flt = F_QH_IDEAL;
break;
}
}
break;
}
break;
}
}
mutex.unlock();
auto it = qh_info.find(cst);
if (it == qh_info.end())
return nullptr;
else
return &it->second;
}
static const QuadInfo *getQMInfo(uint64_t s48)
{
static std::map<uint64_t, QuadInfo> qm_info;
static QMutex mutex;
mutex.lock();
if (qm_info.size() == 0)
{
StructureConfig sc;
getStructureConfig(Monument, MC_NEWEST, &sc);
sc.salt = 0;
for (size_t i = 0, n = sizeof(g_qm_90) / sizeof(uint64_t); i < n; i++)
{
uint64_t s = g_qm_90[i];
QuadInfo *qi = &qm_info[s];
qi->rad = isQuadBase(sc, s, 160);
qi->c = s;
qi->p[0] = getLargeStructurePos(sc, s, 0, 0);
qi->p[1] = getLargeStructurePos(sc, s, 0, 1);
qi->p[2] = getLargeStructurePos(sc, s, 1, 0);
qi->p[3] = getLargeStructurePos(sc, s, 1, 1);
qi->afk = getOptimalAfk(qi->p, 58,0/*23*/,58, &qi->spcnt);
qi->afk.x -= 29;
qi->afk.z -= 29;
qi->typ = Monument;
}
}
mutex.unlock();
auto it = qm_info.find(s48);
if (it == qm_info.end())
return nullptr;
else
return &it->second;
}
static bool isVariantOk(const Condition *c, SearchThreadEnv *e, int stype, int varbiome, Pos *pos)
{
StructureVariant sv;
@ -853,7 +912,7 @@ testCondAt(
int qual, valid;
int xt, zt;
int st;
int i, j, n, icnt;
int i, n, icnt;
int64_t s, r, rmin, rmax;
const uint64_t *seeds;
Pos p[MAX_INSTANCES];
@ -918,16 +977,7 @@ L_qh_any:
s = moveStructure(env->seed, -pc.x, -pc.z);
// find the constellation info
uint64_t cst = (s + sconf.salt) & 0xfffff;
QuadInfo *qi = NULL;
for (j = 0; j < g_qh_c_n; j++)
{
if (qh_constellations[j].c == cst)
{
qi = &qh_constellations[j];
break;
}
}
const QuadInfo *qi = getQHInfo((s + sconf.salt) & 0xfffff);
if (!qi || qi->flt > cond->type)
continue;
// we don't support finding the center of multiple
@ -969,13 +1019,9 @@ L_qm_any:
s = moveStructure(env->seed, -rx, -rz);
if (qmonumentQual(s + sconf.salt) >= qual)
{
getStructurePos(st, env->mc, env->seed, rx+0, rz+0, p+0);
getStructurePos(st, env->mc, env->seed, rx+0, rz+1, p+1);
getStructurePos(st, env->mc, env->seed, rx+1, rz+0, p+2);
getStructurePos(st, env->mc, env->seed, rx+1, rz+1, p+3);
pc = getOptimalAfk(p, 58,23,58, 0);
pc.x -= 29; // monument is centered
pc.z -= 29;
const QuadInfo *qi = getQMInfo(s + sconf.salt);
pc.x = (rx << 9) + qi->afk.x;
pc.z = (rz << 9) + qi->afk.z;
cent[icnt] = pc;
icnt++;
if (imax && icnt >= *imax)

View File

@ -10,8 +10,6 @@
#include <QMap>
#include <atomic>
#define PRECOMPUTE48_BUFSIZ ((int64_t)1 << 30)
enum
{
CAT_NONE,
@ -721,7 +719,7 @@ struct /*__attribute__((packed))*/ Condition
QString toHex() const;
bool readHex(const QString& hex);
QString summary() const;
QString summary(const QFont *font) const;
};
static_assert(

View File

@ -2,22 +2,155 @@
#include "formsearchcontrol.h"
#include "cutil.h"
#include "seedtables.h"
#include "message.h"
#include "aboutdialog.h"
#include <QMessageBox>
#include <QEventLoop>
#include <QApplication>
#include <QStandardPaths>
#include <QElapsedTimer>
#include <QMutex>
#include <QVector>
#include <QDateTime>
SearchMaster::SearchMaster(FormSearchControl *parent)
void Session::writeHeader(QTextStream& stream)
{
stream << "#Version: " << VERS_MAJOR << "." << VERS_MINOR << "." << VERS_PATCH << "\n";
stream << "#Time: " << QDateTime::currentDateTime().toString() << "\n";
// MC version of the session should take priority over the one in the settings
wi.write(stream);
sc.write(stream);
gen48.write(stream);
for (Condition &c : cv)
stream << "#Cond: " << c.toHex() << "\n";
}
bool Session::save(QWidget *widget, QString fnam, bool quiet)
{
QFile file(fnam);
if (!file.open(QIODevice::WriteOnly))
{
if (!quiet)
warn(widget, QApplication::tr("Failed to open file:\n\"%1\"").arg(fnam));
return false;
}
QTextStream stream(&file);
writeHeader(stream);
for (uint64_t s : slist)
stream << QString::asprintf("%" PRId64 "\n", (int64_t)s);
return true;
}
bool Session::load(QWidget *widget, QString fnam, bool quiet)
{
QFile file(fnam);
if (!file.open(QIODevice::ReadOnly))
{
if (!quiet)
warn(widget, QApplication::tr("Failed to open session file:\n\"%1\"").arg(fnam));
return false;
}
int major = 0, minor = 0, patch = 0;
QTextStream stream(&file);
QString line;
line = stream.readLine();
int lno = 1;
if (sscanf(line.toLocal8Bit().data(), "#Version: %d.%d.%d", &major, &minor, &patch) != 3)
{
if (quiet)
return false;
QString msg = QApplication::tr("File does not look like a session file.\n"
"Progress may be incomplete or broken.\n\n"
"Continue anyway?");
int button = warn(widget, msg, QMessageBox::Abort|QMessageBox::Yes);
if (button == QMessageBox::Abort)
return false;
}
else if (cmpVers(major, minor, patch) > 0)
{
if (quiet)
return false;
QString msg = QApplication::tr("Session file was created with a newer version.\n"
"Progress may be incomplete or broken.\n\n"
"Continue loading progress anyway?");
int button = warn(widget, msg, QMessageBox::Abort|QMessageBox::Yes);
if (button == QMessageBox::Abort)
return false;
}
while (stream.status() == QTextStream::Ok && !stream.atEnd())
{
lno++;
line = stream.readLine();
if (line.isEmpty()) continue;
if (line.startsWith("#Time:")) continue;
if (line.startsWith("#Title:")) continue;
if (line.startsWith("#Desc:")) continue;
if (sc.read(line)) continue;
if (gen48.read(line)) continue;
if (wi.read(line)) continue;
if (line.startsWith("#Cond:"))
{ // Conditions
Condition c;
if (c.readHex(line.mid(6).trimmed()))
{
cv.push_back(c);
}
else
{
if (quiet)
return false;
QString msg = QApplication::tr("Condition [%1] at line %2 is not supported.\n\n"
"Continue anyway?");
int button = warn(widget, msg.arg(c.save).arg(lno), QMessageBox::Abort|QMessageBox::Yes);
if (button == QMessageBox::Abort)
return false;
}
}
else
{ // Seeds
QByteArray ba = line.toLocal8Bit();
const char *p = ba.data();
uint64_t s;
if (sscanf(p, "%" PRId64, (int64_t*)&s) == 1)
{
slist.push_back(s);
}
else
{
if (quiet)
return false;
QString msg = QApplication::tr("Failed to parse line %1 of file:\n%2\n\n"
"Continue anyway?");
int button = warn(widget, msg.arg(lno).arg(line), QMessageBox::Abort|QMessageBox::Yes);
if (button == QMessageBox::Abort)
return false;
}
}
}
return true;
}
SearchMaster::SearchMaster(QWidget *parent)
: QThread(parent)
, parent(parent)
, mutex()
, abort()
, timer()
, proghist()
, progtimer()
, itemtimer()
, count()
, env()
, searchtype()
@ -43,39 +176,27 @@ SearchMaster::~SearchMaster()
stop();
}
bool SearchMaster::set(
WorldInfo wi,
const SearchConfig& sc,
const Gen48Config& gen48,
const Config& config,
std::vector<uint64_t>& slist,
const QVector<Condition>& cv
)
bool SearchMaster::set(QWidget *widget, const Session& s)
{
(void) config;
char refbuf[100] = {};
char disabled[100] = {};
for (const Condition& c : cv)
for (const Condition& c : s.cv)
if (c.meta & Condition::DISABLED)
disabled[c.save] = 1;
for (const Condition& c : cv)
for (const Condition& c : s.cv)
{
char cid[8];
snprintf(cid, sizeof(cid), "[%02d]", c.save);
if (c.save < 1 || c.save > 99)
{
QMessageBox::warning(parent, tr("Warning"),
tr("Condition with invalid ID %1.").arg(cid));
warn(widget, tr("Condition with invalid ID %1.").arg(cid));
return false;
}
if (c.type < 0 || c.type >= FILTER_MAX)
{
QMessageBox::warning(parent, tr("Error"),
tr("Encountered invalid filter type %1 in condition ID %2.")
.arg(c.type).arg(cid));
warn(widget, tr("Encountered invalid filter type %1 in condition ID %2.").arg(c.type).arg(cid));
return false;
}
if (disabled[c.save])
@ -85,39 +206,32 @@ bool SearchMaster::set(
if (c.relative && refbuf[c.relative] == 0)
{
QMessageBox::warning(parent, tr("Warning"),
tr("Condition with ID %1 has a broken reference position:\n"
"condition missing or out of order.").arg(cid));
warn(widget, tr("Condition with ID %1 has a broken reference position:\n"
"condition missing or out of order.").arg(cid));
return false;
}
if (++refbuf[c.save] > 1)
{
QMessageBox::warning(parent, tr("Warning"),
tr("More than one condition with ID %1.").arg(cid));
warn(widget, tr("More than one condition with ID %1.").arg(cid));
return false;
}
if (c.relative && disabled[c.relative])
{
int button = QMessageBox::information(NULL, tr("Warning"),
tr("Condition %1 has been indirectly disabled by reference.")
.arg(cid), QMessageBox::Abort|QMessageBox::Ignore);
int button = info(widget, tr("Condition %1 has been indirectly disabled by reference.").arg(cid),
QMessageBox::Abort|QMessageBox::Ignore);
if (button == QMessageBox::Abort)
return false;
}
if (wi.mc < finfo.mcmin)
if (s.wi.mc < finfo.mcmin)
{
const char *mcs = mc2str(finfo.mcmin);
QMessageBox::warning(parent, tr("Warning"),
tr("Condition %1 requires a minimum Minecraft version of %2.")
.arg(cid, mcs));
warn(widget, tr("Condition %1 requires a minimum Minecraft version of %2.").arg(cid, mcs));
return false;
}
if (wi.mc > finfo.mcmax)
if (s.wi.mc > finfo.mcmax)
{
const char *mcs = mc2str(finfo.mcmax);
QMessageBox::warning(parent, tr("Warning"),
tr("Condition %1 not available for Minecraft versions above %2.")
.arg(cid, mcs));
warn(widget, tr("Condition %1 not available for Minecraft versions above %2.").arg(cid, mcs));
return false;
}
if (finfo.cat == CAT_BIOMES &&
@ -131,41 +245,37 @@ bool SearchMaster::set(
uint64_t m = c.biomeToFindM;
if ((c.biomeToExcl & b) || (c.biomeToExclM & m))
{
QMessageBox::warning(parent, tr("Warning"),
tr("Biome condition with ID %1 has contradicting "
"flags for include and exclude.").arg(cid));
warn(widget, tr("Biome condition with ID %1 has contradicting flags for include and exclude.").arg(cid));
return false;
}
if ((b | m | c.biomeToExcl | c.biomeToExclM) == 0)
{
int button = QMessageBox::information(parent, tr("Info"),
tr("Biome condition with ID %1 specifies no biomes.")
.arg(cid), QMessageBox::Abort|QMessageBox::Ignore);
int button = info(widget, tr("Biome condition with ID %1 specifies no biomes.").arg(cid),
QMessageBox::Abort|QMessageBox::Ignore);
if (button == QMessageBox::Abort)
return false;
}
int layerId = finfo.layer;
if (layerId == 0 && wi.mc <= MC_1_17)
if (layerId == 0 && s.wi.mc <= MC_1_17)
{
Generator tmp;
setupGenerator(&tmp, wi.mc, 0);
setupGenerator(&tmp, s.wi.mc, 0);
const Layer *l = getLayerForScale(&tmp, finfo.step);
if (l)
layerId = l - tmp.ls.layers;
}
uint64_t ab, am;
uint32_t flags = 0;
getAvailableBiomes(&ab, &am, layerId, wi.mc, flags);
getAvailableBiomes(&ab, &am, layerId, s.wi.mc, flags);
b ^= (ab & b);
m ^= (am & m);
if (b || m)
{
int cnt = __builtin_popcountll(b) + __builtin_popcountll(m);
QString msg = tr("Biome condition with ID %1 includes %n "
"biome(s) that do not generate in MC %2.", "", cnt)
.arg(cid, mc2str(wi.mc));
QMessageBox::warning(parent, tr("Warning"), msg);
QString msg = tr("Biome condition with ID %1 includes %n biome(s) "
"that do not generate in MC %2.", "", cnt);
warn(widget, msg.arg(cid, mc2str(s.wi.mc)));
return false;
}
}
@ -175,10 +285,9 @@ bool SearchMaster::set(
int h = c.z2 - c.z1 + 1;
if (c.count > w * h)
{
QMessageBox::warning(parent, tr("Warning"),
tr("Temperature category condition with ID %1 has too "
"many restrictions (%2) for the area (%3 x %4).")
.arg(cid).arg(c.count).arg(w).arg(h));
QString msg = tr("Temperature category condition with ID %1 has too "
"many restrictions (%2) for the area (%3 x %4).");
warn(widget, msg.arg(cid).arg(c.count).arg(w).arg(h));
return false;
}
}
@ -186,46 +295,41 @@ bool SearchMaster::set(
{
if (c.count >= 128)
{
QMessageBox::warning(parent, tr("Warning"),
tr("Structure condition %1 checks for too many instances (>= 128).")
.arg(cid));
warn(widget, tr("Structure condition %1 checks for too many instances (>= 128).").arg(cid));
return false;
}
}
if (c.skipref && c.rmax == 0 && c.x1 == 0 && c.x2 == 0 && c.z1 == 0 && c.z2 == 0)
{
QMessageBox::warning(parent, tr("Warning"),
tr("Condition %1 ignores its only location of size 1.")
.arg(cid));
warn(widget, tr("Condition %1 ignores its only location of size 1.").arg(cid));
return false;
}
}
QString err = condtree.set(cv, wi.mc);
QString err = condtree.set(s.cv, s.wi.mc);
if (err.isEmpty())
{
err = env.init(wi.mc, wi.large, &condtree);
err = env.init(s.wi.mc, s.wi.large, &condtree);
}
if (!err.isEmpty())
{
QMessageBox::warning(parent, tr("Warning"),
tr("Failed to setup search environment:\n%1").arg(err));
warn(widget, tr("Failed to setup search environment:\n%1").arg(err));
return false;
}
this->searchtype = sc.searchtype;
this->mc = wi.mc;
this->large = wi.large;
this->itemsize = 1; //config.seedsPerItem;
this->threadcnt = sc.threads;
this->slist = slist;
this->gen48 = gen48;
this->searchtype = s.sc.searchtype;
this->mc = s.wi.mc;
this->large = s.wi.large;
this->itemsize = 1;
this->threadcnt = s.sc.threads;
this->slist = s.slist;
this->gen48 = s.gen48;
this->idx = 0;
this->scnt = ~(uint64_t)0;
this->prog = 0;
this->seed = sc.startseed;
this->smin = sc.smin;
this->smax = sc.smax;
this->seed = s.sc.startseed;
this->smin = s.sc.smin;
this->smax = s.sc.smax;
this->isdone = false;
this->abort = false;
return true;
@ -282,24 +386,30 @@ static void genQHBases(QObject *qtobj, int qual, uint64_t salt, std::vector<uint
if ((qb = loadSavedSeeds(fnam.data(), &qn)) == NULL)
{
printf("Writing quad-protobases to: %s\n", fnam.data());
printf("[INFO]: Writing quad-protobases to: %s\n", fnam.data());
fflush(stdout);
QMetaObject::invokeMethod(qtobj, "openProtobaseMsg", Qt::QueuedConnection, Q_ARG(QString, path));
if (qtobj)
QMetaObject::invokeMethod(qtobj, "openProtobaseMsg", Qt::QueuedConnection, Q_ARG(QString, path));
int threads = QThread::idealThreadCount();
int err = searchAll48(&qb, &qn, fnam.data(), threads, lbset, lbcnt, 20, check, NULL);
if (err)
{
QMetaObject::invokeMethod(
qtobj, "warning", Qt::BlockingQueuedConnection,
Q_ARG(QString, SearchMaster::tr("Failed to generate protobases.")));
printf("[WARN]: Failed to generate protobases.\n");
if (qtobj)
{
QMetaObject::invokeMethod(
qtobj, "warning", Qt::BlockingQueuedConnection,
Q_ARG(QString, SearchMaster::tr("Failed to generate protobases.")));
}
return;
}
else
{
QMetaObject::invokeMethod(qtobj, "closeProtobaseMsg", Qt::BlockingQueuedConnection);
if (qtobj)
QMetaObject::invokeMethod(qtobj, "closeProtobaseMsg", Qt::BlockingQueuedConnection);
}
}
else
@ -355,10 +465,27 @@ static bool applyTranspose(std::vector<uint64_t>& slist,
return !slist.empty();
}
void SearchMaster::presearch()
void SearchMaster::presearch(QObject *qtobj)
{
uint64_t sstart = seed;
if (gen48.mode == GEN48_AUTO)
{ // resolve automatic mode
for (const Condition& c : qAsConst(condtree.condvec))
{
if (c.type >= F_QH_IDEAL && c.type <= F_QH_BARELY)
{
gen48.mode = GEN48_QH;
break;
}
else if (c.type >= F_QM_95 && c.type <= F_QM_90)
{
gen48.mode = GEN48_QM;
break;
}
}
}
if (searchtype != SEARCH_LIST)
{
if (gen48.mode == GEN48_QH)
@ -373,7 +500,7 @@ void SearchMaster::presearch()
salt = sconf.salt;
}
slist.clear();
genQHBases(parent, gen48.qual, salt, slist);
genQHBases(qtobj, gen48.qual, salt, slist);
}
else if (gen48.mode == GEN48_QM)
{
@ -494,7 +621,7 @@ void SearchMaster::presearch()
void SearchMaster::run()
{
presearch();
presearch(parent());
stop();
for (int i = 0; i < threadcnt; i++)
@ -502,7 +629,7 @@ void SearchMaster::run()
SearchWorker *worker = new SearchWorker(this);
QObject::connect(
worker, &SearchWorker::result,
parent, &FormSearchControl::onSearchResult,
this, &SearchMaster::onWorkerResult,
Qt::BlockingQueuedConnection);
QObject::connect(
worker, &SearchWorker::finished,
@ -515,7 +642,9 @@ void SearchMaster::run()
QMutexLocker locker(&mutex);
abort = false;
timer.start();
proghist.clear();
progtimer.start();
itemtimer.start();
count = 0;
for (SearchWorker *worker: workers)
@ -524,7 +653,6 @@ void SearchMaster::run()
}
}
void SearchMaster::stop()
{
abort = true;
@ -548,6 +676,8 @@ void SearchMaster::stop()
}
if (!running)
break;
if (parent() == nullptr)
continue;
int button = 0;
Qt::ConnectionType connectiontype = Qt::BlockingQueuedConnection;
if (QThread::currentThread() == QApplication::instance()->thread())
@ -555,7 +685,7 @@ void SearchMaster::stop()
connectiontype = Qt::DirectConnection;
}
QMetaObject::invokeMethod(
parent, "warning", connectiontype,
parent(), "warning", connectiontype,
Q_RETURN_ARG(int, button),
Q_ARG(QString, tr("Failed to stop %n worker thread(s).\n"
"Keep waiting for threads to stop?", "", running)),
@ -580,7 +710,19 @@ void SearchMaster::stop()
emit searchFinish(false);
}
bool SearchMaster::getProgress(uint64_t *prog, uint64_t *end, uint64_t *seed)
static QString getAbbrNum(double x)
{
if (x >= 10e9)
return QString::asprintf("%.1fG", x * 1e-9);
if (x >= 10e6)
return QString::asprintf("%.1fM", x * 1e-6);
if (x >= 10e3)
return QString::asprintf("%.1fK", x * 1e-3);
return QString::asprintf("%.2f", x);
}
bool SearchMaster::getProgress(QString *status, uint64_t *prog, uint64_t *end, uint64_t *seed, qreal *min, qreal *avg, qreal *max)
{
if (!mutex.tryLock(10))
{
@ -597,6 +739,7 @@ bool SearchMaster::getProgress(uint64_t *prog, uint64_t *end, uint64_t *seed)
*prog = this->prog;
*end = this->scnt;
*seed = this->seed;
*min = *avg = *max = nan("");
bool valid = false;
for (SearchWorker *worker: workers)
@ -608,7 +751,86 @@ bool SearchMaster::getProgress(uint64_t *prog, uint64_t *end, uint64_t *seed)
valid = true;
}
}
if (isdone)
{
*prog = this->scnt;
}
mutex.unlock();
// track the progress over a few seconds so we can estimate the search speed
enum { SAMPLE_SEC = 20 };
if (valid)
{
TProg tp = { (uint64_t) progtimer.nsecsElapsed(), *prog };
proghist.push_front(tp);
while (proghist.size() > 1 && proghist.back().ns < tp.ns - SAMPLE_SEC*1e9)
proghist.pop_back();
}
if (proghist.size() > 1 && proghist.front().ns > proghist.back().ns)
{
std::vector<qreal> samples;
samples.reserve(proghist.size());
auto it_prev = proghist.begin();
auto it = it_prev;
while (++it != proghist.end())
{
qreal dp = it_prev->prog - it->prog;
qreal dt = 1e-9 * (it_prev->ns - it->ns);
if (dt > 0)
samples.push_back(dp / dt);
it_prev = it;
}
std::sort(samples.begin(), samples.end());
qreal speedtot = 0;
qreal weightot = 1e-6;
int n = (int) samples.size();
int r = (int) (n / SAMPLE_SEC);
int has_zeros = 0;
for (int i = -r; i <= r; i++)
{
int j = n/2 + i;
if (j < 0 || j >= n)
continue;
has_zeros += samples[j] == 0;
speedtot += samples[j];
weightot += 1.0;
}
*min = samples[n*1/4]; // lower quartile
*avg = speedtot / weightot; // median
*max = samples[n*3/4]; // upper quartile
if (*avg && has_zeros)
{ // probably a slow sampling regime, use whole range for estimate
speedtot = 0;
for (qreal s : samples)
speedtot += s;
*avg = speedtot / n;
}
}
qreal remain = ((qreal)*end - *prog) / (*avg + 1e-6);
QString eta;
if (remain >= 3600*24*1000)
eta = "years";
else if (remain > 0)
{
int s = (int) remain;
if (s > 86400)
eta = QString("%1d:%2").arg(s / 86400).arg((s % 86400) / 3600, 2, 10, QLatin1Char('0'));
else if (s > 3600)
eta = QString("%1h:%2").arg(s / 3600).arg((s % 3600) / 60, 2, 10, QLatin1Char('0'));
else
eta = QString("%1:%2").arg(s / 60).arg(s % 60, 2, 10, QLatin1Char('0'));
}
*status = QString("seeds/sec: %1 min: %2 max: %3 isize: %4 eta: %5")
.arg(getAbbrNum(*avg), -8)
.arg(getAbbrNum(*min), -8)
.arg(getAbbrNum(*max), -8)
.arg(itemsize, -3)
.arg(eta);
return valid;
}
@ -620,7 +842,7 @@ bool SearchMaster::requestItem(SearchWorker *item)
QMutexLocker locker(&mutex);
// check if we should adjust the item size
uint64_t nsec = timer.nsecsElapsed();
uint64_t nsec = itemtimer.nsecsElapsed();
count++;
if (nsec > 0.1e9)
{
@ -628,7 +850,7 @@ bool SearchMaster::requestItem(SearchWorker *item)
itemsize /= 2;
if (count > 1e3 && itemsize < 0x10000)
itemsize *= 2;
timer.start();
itemtimer.start();
count = 0;
}
@ -723,10 +945,16 @@ bool SearchMaster::requestItem(SearchWorker *item)
return true;
}
void SearchMaster::onWorkerResult(uint64_t seed)
{
emit searchResult(seed);
}
void SearchMaster::onWorkerFinished()
{
QMutexLocker locker(&mutex);
if (workers.empty())
return;
for (SearchWorker *worker : workers)
if (!worker->isFinished())
return;

View File

@ -8,46 +8,72 @@
#include <QMutex>
#include <QVector>
#include <QElapsedTimer>
#include <QTimer>
#include <QMessageBox>
#include <deque>
struct Session
{
void writeHeader(QTextStream& stream);
bool save(QWidget *widget, QString fnam, bool quiet);
bool load(QWidget *widget, QString fnam, bool quiet);
WorldInfo wi;
SearchConfig sc;
Gen48Config gen48;
QVector<Condition> cv;
std::vector<uint64_t> slist;
};
class FormSearchControl;
struct SearchWorker;
struct SearchMaster : QThread
{
Q_OBJECT
public:
SearchMaster(FormSearchControl *parent);
SearchMaster(QWidget *parent);
virtual ~SearchMaster();
bool set(WorldInfo wi, const SearchConfig& sc, const Gen48Config& gen48, const Config& config,
std::vector<uint64_t>& slist, const QVector<Condition>& cv);
bool set(QWidget *widget, const Session& s);
void presearch();
void presearch(QObject *qtobj);
virtual void run() override;
void stop();
// Determines the lowest seed/prog that is about to be processed.
// (This is the point at which you would want to reload a previous search.)
bool getProgress(uint64_t *prog, uint64_t *end, uint64_t *seed);
// Get search progress:
// status : progress status summary
// prog : scheduled progress in search space
// end : size of search space
// seed : current seed to be processed
// Get the search speed (provided it is called at regular intervals):
// min,max : lower and upper search speed quartiles
// avg : search speed average
bool getProgress(QString *status, uint64_t *prog, uint64_t *end, uint64_t *seed, qreal *min, qreal *avg, qreal *max);
bool requestItem(SearchWorker *item);
public slots:
void onWorkerResult(uint64_t seed);
void onWorkerFinished();
signals:
void searchResult(uint64_t seed);
void searchFinish(bool done);
public:
FormSearchControl * parent;
struct TProg { uint64_t ns, prog; };
public:
std::vector<SearchWorker*> workers;
QMutex mutex;
std::atomic_bool abort;
QElapsedTimer timer;
std::deque<TProg> proghist;
QElapsedTimer progtimer;
QElapsedTimer itemtimer;
uint64_t count;
SearchThreadEnv env;
@ -58,7 +84,7 @@ public:
ConditionTree condtree;
int itemsize; // number of seeds per search item
int threadcnt; // numbr of worker threads
Gen48Config gen48; // 48-bit generator settings
Gen48Config gen48; // 48-bit generator settings
std::vector<uint64_t> slist; // candidate list
uint64_t idx; // index within candidate list
uint64_t scnt; // search space size

View File

@ -37,7 +37,7 @@ StructureDialog::StructureDialog(QWidget *parent)
icon->setPixmap(getMapIcon(opt));
grid->addWidget(icon, i, j++);
QString name = struct2str(mapopt2stype(opt));
QString name = mapopt2display(opt);
QLabel *label = new QLabel(name + ":");
grid->addWidget(label, i, j++);

View File

@ -2,6 +2,7 @@
#include "ui_tabbiomes.h"
#include "cutil.h"
#include "world.h"
#include "message.h"
#include <QFileDialog>
#include <QFileInfo>
@ -18,7 +19,7 @@ void AnalysisBiomes::run()
Generator g;
setupGenerator(&g, wi.mc, wi.large);
for (idx = 0; idx < seeds.size(); idx++)
for (idx = 0; idx < (long)seeds.size(); idx++)
{
if (stop) break;
wi.seed = seeds[idx];
@ -169,7 +170,7 @@ QVariant BiomeTableModel::headerData(int section, Qt::Orientation orientation, i
if (section < ids.size())
{
if (role == Qt::DisplayRole)
return QVariant::fromValue(QString(biome2str(cmp.mc, ids[section])));
return QVariant::fromValue(QString(getBiomeDisplay(cmp.mc, ids[section])));
else
return QVariant::fromValue(ids[section]);
}
@ -338,7 +339,6 @@ TabBiomes::TabBiomes(MainWindow *parent)
//QHeaderView *header = ui->table->horizontalHeader();
connect(header, &QHeaderView::sortIndicatorChanged, this, &TabBiomes::onTableSort);
ui->table->setFont(*gp_font_mono);
ui->table->setSortingEnabled(true);
ui->treeLocate->setColumnWidth(0, 160);
@ -363,10 +363,10 @@ TabBiomes::TabBiomes(MainWindow *parent)
for (int id = 0; id < 256; id++)
{
const char *s;
if ((s = biome2str(MC_1_17, id)))
QString s;
if (!(s = getBiomeDisplay(MC_1_17, id)).isEmpty())
str2biome[s] = id;
if ((s = biome2str(MC_NEWEST, id)))
if (!(s = getBiomeDisplay(MC_NEWEST, id)).isEmpty())
str2biome[s] = id;
}
@ -454,10 +454,10 @@ void TabBiomes::refreshBiomes(int activeid)
std::sort(ids.begin(), ids.end(), cmp);
ui->comboBiome->clear();
for (int i : ids)
ui->comboBiome->addItem(getBiomeIcon(i), biome2str(wi.mc, i), QVariant::fromValue(i));
ui->comboBiome->addItem(getBiomeIcon(i), getBiomeDisplay(wi.mc, i), QVariant::fromValue(i));
if (activeid >= 0)
{
int idx = ui->comboBiome->findText(biome2str(wi.mc, activeid));
int idx = ui->comboBiome->findText(getBiomeDisplay(wi.mc, activeid));
ui->comboBiome->setCurrentIndex(idx);
}
}
@ -587,7 +587,7 @@ void TabBiomes::onBufferTimeout()
qbufl.clear();
}
QString progress = QString::asprintf(" (%d/%d)", thread.idx.load(), thread.seeds.size());
QString progress = QString::asprintf(" (%ld/%zu)", thread.idx.load(), thread.seeds.size());
ui->pushStart->setText(tr("Stop") + progress);
QApplication::processEvents(); // force processing of events so we can time correctly
@ -613,7 +613,7 @@ void TabBiomes::on_pushStart_clicked()
parent->getSeed(&thread.wi);
thread.seeds.clear();
if (ui->comboSeedSource->currentIndex() == 0)
thread.seeds.append(thread.wi.seed);
thread.seeds.push_back(thread.wi.seed);
else
thread.seeds = parent->formControl->getResults();
@ -650,7 +650,7 @@ void TabBiomes::on_pushStart_clicked()
{
QString msg = tr("The locate biome feature is limited to an area "
"size smaller than the integer limit: (%1 x %2) > %3.");
parent->warning(msg.arg(sx).arg(sz).arg(INT_MAX));
warn(parent, msg.arg(sx).arg(sz).arg(INT_MAX));
return;
}
@ -686,7 +686,7 @@ void TabBiomes::on_pushStart_clicked()
ui->pushExport->setEnabled(false);
ui->pushStart->setChecked(true);
QString progress = QString::asprintf(" (0/%d)", thread.seeds.size());
QString progress = QString::asprintf(" (0/%zu)", thread.seeds.size());
ui->pushStart->setText(tr("Stop") + progress);
thread.start();
}
@ -716,7 +716,7 @@ void TabBiomes::on_pushExport_clicked()
if (!file.open(QIODevice::WriteOnly))
{
parent->warning(tr("Failed to open file for export:\n\"%1\"").arg(fnam));
warn(parent, tr("Failed to open file for export:\n\"%1\"").arg(fnam));
return;
}
@ -821,7 +821,7 @@ void TabBiomes::on_radioFullSample_toggled(bool checked)
void TabBiomes::on_lineBiomeSize_textChanged(const QString &text)
{
double area = text.toInt();
ui->labelBiomeSize->setText(QString::asprintf(tr("(%g sq. chunks)").toStdString().c_str(), area / 16));
ui->labelBiomeSize->setText(tr("(%1 sq. chunks)").arg(area / 16));
}
void TabBiomes::on_treeLocate_itemClicked(QTreeWidgetItem *item, int column)

View File

@ -32,10 +32,10 @@ signals:
void seedItem(QTreeWidgetItem *item);
public:
QVector<uint64_t> seeds;
std::vector<uint64_t> seeds;
WorldInfo wi;
std::atomic_bool stop;
std::atomic_int idx;
std::atomic_long idx;
int dims[3];
struct Dat {
int x1, z1, x2, z2;

View File

@ -412,6 +412,11 @@
</item>
<item row="2" column="0">
<widget class="QTableView" name="table">
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>

View File

@ -2,6 +2,7 @@
#include "ui_tabstructures.h"
#include "cutil.h"
#include "message.h"
#include <QTreeWidgetItem>
#include <QFileDialog>
@ -34,7 +35,7 @@ void AnalysisStructures::run()
Generator g;
setupGenerator(&g, wi.mc, wi.large);
for (idx = 0; idx < seeds.size(); idx++)
for (idx = 0; idx < (long)seeds.size(); idx++)
{
if (stop) break;
wi.seed = seeds[idx];
@ -169,7 +170,7 @@ void AnalysisStructures::runStructs(Generator *g)
return;
}
if (stop)
seeditem->setText(0, QString::asprintf("%" PRId64, wi.seed) + " " + tr("(incomplete)"));
seeditem->setText(0, QString::asprintf("%" PRId64, wi.seed) + " " + "(incomplete)");
emit itemDone(seeditem);
}
@ -192,9 +193,9 @@ void AnalysisStructures::runQuads(Generator *g)
{
QString label;
if (qi.typ == Swamp_Hut)
label = tr("quad-hut");
label = "quad-hut";
else
label = tr("quad-monument");
label = "quad-monument";
QTreeWidgetItem *item = new QTreeWidgetItem(seeditem);
@ -363,7 +364,7 @@ void TabStructures::onBufferTimeout()
ui->treeQuads->setSortingEnabled(true);
qbufq.clear();
}
QString progress = QString::asprintf(" (%d/%d)", thread.idx.load(), thread.seeds.size());
QString progress = QString::asprintf(" (%d/%zu)", thread.idx.load(), thread.seeds.size());
ui->pushStart->setText(tr("Stop") + progress);
QApplication::processEvents(); // force processing of events so we can time correctly
@ -411,7 +412,7 @@ void TabStructures::on_pushStart_clicked()
parent->getSeed(&thread.wi);
thread.seeds.clear();
if (ui->comboSeedSource->currentIndex() == 0)
thread.seeds.append(thread.wi.seed);
thread.seeds.push_back(thread.wi.seed);
else
thread.seeds = parent->formControl->getResults();
@ -449,7 +450,7 @@ void TabStructures::on_pushStart_clicked()
ui->pushExport->setEnabled(false);
ui->pushStart->setChecked(true);
QString progress = QString::asprintf(" (0/%d)", thread.seeds.size());
QString progress = QString::asprintf(" (0/%zu)", thread.seeds.size());
ui->pushStart->setText(tr("Stop") + progress);
thread.start();
}
@ -479,7 +480,7 @@ void TabStructures::on_pushExport_clicked()
if (!file.open(QIODevice::WriteOnly))
{
parent->warning(tr("Failed to open file for export:\n\"%1\"").arg(fnam));
warn(parent, tr("Failed to open file for export:\n\"%1\"").arg(fnam));
return;
}

View File

@ -24,7 +24,7 @@ signals:
void quadDone(QTreeWidgetItem *item);
public:
QVector<uint64_t> seeds;
std::vector<uint64_t> seeds;
WorldInfo wi;
std::atomic_bool stop;
std::atomic_int idx;

View File

@ -1,6 +1,7 @@
#include "tabtriggers.h"
#include "ui_tabtriggers.h"
#include "message.h"
#include "cutil.h"
#include "config.h"
@ -27,8 +28,9 @@ QTreeWidgetItem *setConditionTreeItems(ConditionTree& ctree, int node, int64_t s
else
{
item = new QTreeWidgetItem(parent);
QFont font = item->font(1);
item->setText(0, "-");
item->setText(1, c.summary());
item->setText(1, c.summary(&font));
if ((p.x == -1 && p.z == -1) || c.type == F_LOGIC_NOT)
posval = false;
@ -54,7 +56,7 @@ void AnalysisTriggers::run()
{
stop = false;
for (idx = 0; idx < seeds.size(); idx++)
for (idx = 0; idx < (long)seeds.size(); idx++)
{
if (stop) break;
int64_t seed = seeds[idx];
@ -113,7 +115,7 @@ TabTriggers::TabTriggers(MainWindow *parent)
ui->treeWidget->setColumnWidth(3, 65);
ui->treeWidget->setSortingEnabled(false); // sortable triggers are not necessary
connect(&thread, &AnalysisTriggers::warning, parent, &MainWindow::warning, Qt::BlockingQueuedConnection);
connect(&thread, &AnalysisTriggers::warning, this, &TabTriggers::warning, Qt::BlockingQueuedConnection);
connect(&thread, &AnalysisTriggers::itemDone, this, &TabTriggers::onAnalysisItemDone, Qt::BlockingQueuedConnection);
connect(&thread, &AnalysisTriggers::finished, this, &TabTriggers::onAnalysisFinished);
}
@ -136,6 +138,11 @@ void TabTriggers::load(QSettings& settings)
ui->comboSeedSource->setCurrentIndex(idx);
}
int TabTriggers::warning(QString text, QMessageBox::StandardButtons buttons)
{
return warn(parent, text, buttons);
}
void TabTriggers::onAnalysisItemDone(QTreeWidgetItem *item)
{
qbuf.push_back(item);
@ -165,7 +172,7 @@ void TabTriggers::onBufferTimeout()
ui->treeWidget->addTopLevelItems(qbuf);
ui->treeWidget->setUpdatesEnabled(true);
QString progress = QString::asprintf(" (%d/%d)", thread.idx.load(), thread.seeds.size());
QString progress = QString::asprintf(" (%ld/%zu)", thread.idx.load(), thread.seeds.size());
ui->pushStart->setText(tr("Stop") + progress);
qbuf.clear();
@ -194,7 +201,7 @@ void TabTriggers::on_pushStart_clicked()
thread.conds = parent->formCond->getConditions();
thread.seeds.clear();
if (ui->comboSeedSource->currentIndex() == 0)
thread.seeds.append(thread.wi.seed);
thread.seeds.push_back(thread.wi.seed);
else
thread.seeds = parent->formControl->getResults();
@ -204,7 +211,7 @@ void TabTriggers::on_pushStart_clicked()
ui->pushExport->setEnabled(false);
ui->pushStart->setChecked(true);
QString progress = QString::asprintf(" (0/%d)", thread.seeds.size());
QString progress = QString::asprintf(" (0/%zu)", thread.seeds.size());
ui->pushStart->setText(tr("Stop") + progress);
thread.start();
}
@ -263,7 +270,7 @@ void TabTriggers::on_pushExport_clicked()
if (!file.open(QIODevice::WriteOnly))
{
parent->warning(tr("Failed to open file for export:\n\"%1\"").arg(fnam));
warn(parent, tr("Failed to open file for export:\n\"%1\"").arg(fnam));
return;
}

View File

@ -28,10 +28,10 @@ signals:
public:
QVector<Condition> conds;
QVector<uint64_t> seeds;
std::vector<uint64_t> seeds;
WorldInfo wi;
std::atomic_bool stop;
std::atomic_int idx;
std::atomic_long idx;
};
@ -47,6 +47,7 @@ public:
virtual void load(QSettings& settings) override;
private slots:
int warning(QString text, QMessageBox::StandardButtons buttons);
void onAnalysisItemDone(QTreeWidgetItem *item);
void onAnalysisFinished();
void onBufferTimeout();

View File

@ -83,8 +83,8 @@ void replyFinished(QNetworkReply *reply, bool quiet)
}
QMessageBox::StandardButton answer = QMessageBox::question(
NULL, QApplication::translate("UpdaterDialog", "New Version"),
QApplication::translate("UpdaterDialog", "<p>A new version: <b>%1</b> is available.</p><p>Open the download page in browser?</p>").arg(newest),
NULL, QObject::tr("New Version"),
QObject::tr("<p>A new version: <b>%1</b> is available.</p><p>Open the download page in browser?</p>").arg(newest),
QMessageBox::Yes|QMessageBox::No);
if (answer == QMessageBox::Yes)

View File

@ -23,30 +23,8 @@ const QPixmap& getMapIcon(int opt, VarPos *vp)
if (!init)
{
init = true;
icons[D_DESERT] = QPixmap(":/icons/desert.png");
icons[D_JUNGLE] = QPixmap(":/icons/jungle.png");
icons[D_IGLOO] = QPixmap(":/icons/igloo.png");
icons[D_HUT] = QPixmap(":/icons/hut.png");
icons[D_VILLAGE] = QPixmap(":/icons/village.png");
icons[D_MANSION] = QPixmap(":/icons/mansion.png");
icons[D_MONUMENT] = QPixmap(":/icons/monument.png");
icons[D_RUINS] = QPixmap(":/icons/ruins.png");
icons[D_SHIPWRECK] = QPixmap(":/icons/shipwreck.png");
icons[D_TREASURE] = QPixmap(":/icons/treasure.png");
icons[D_MINESHAFT] = QPixmap(":/icons/mineshaft.png");
icons[D_WELL] = QPixmap(":/icons/well.png");
icons[D_GEODE] = QPixmap(":/icons/geode.png");
icons[D_OUTPOST] = QPixmap(":/icons/outpost.png");
icons[D_ANCIENTCITY]= QPixmap(":/icons/ancient_city.png");
icons[D_TRAIL] = QPixmap(":/icons/trail.png");
icons[D_PORTAL] = QPixmap(":/icons/portal.png");
icons[D_PORTALN] = QPixmap(":/icons/portal.png");
icons[D_SPAWN] = QPixmap(":/icons/spawn.png");
icons[D_STRONGHOLD] = QPixmap(":/icons/stronghold.png");
icons[D_FORTESS] = QPixmap(":/icons/fortress.png");
icons[D_BASTION] = QPixmap(":/icons/bastion.png");
icons[D_ENDCITY] = QPixmap(":/icons/endcity.png");
icons[D_GATEWAY] = QPixmap(":/icons/gateway.png");
for (int sopt = D_DESERT; sopt <= D_STRONGHOLD; sopt++)
icons[sopt] = QPixmap(QString(":/icons/") + mapopt2str(sopt) + ".png");
iconzvil = QPixmap(":/icons/zombie.png");
icongiant = QPixmap(":/icons/portal_giant.png");
iconship = QPixmap(":/icons/end_ship.png");
@ -903,8 +881,7 @@ QString QWorld::getBiomeName(Pos p)
c += "." + QString::number(lopt.activeDisp());
return c + "=" + QString::number(id);
}
const char *s = biome2str(wi.mc, id);
QString ret = s ? s : "";
QString ret = getBiomeDisplay(wi.mc, id);
if (lopt.mode == LOPT_HEIGHT_4 && dim == DIM_OVERWORLD)
ret = QString::asprintf("Y~%d ", estimateSurface(p)) + ret;
return ret;
@ -1169,7 +1146,7 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz,
QFont oldfont = painter.font();
QFont smallfont = oldfont;
smallfont.setPointSize(8);
smallfont.setPointSize(oldfont.pointSize() - 2);
painter.setFont(smallfont);
int gridpix = 128;