mirror of
https://github.com/overte-org/overte.git
synced 2025-05-29 14:30:34 +02:00
Merge branch 'baseball' of github.com:Atlante45/hifi into baseball
This commit is contained in:
commit
779cadd5bd
9 changed files with 103 additions and 193 deletions
|
@ -403,7 +403,7 @@ void Agent::sendPingRequests() {
|
|||
case NodeType::AvatarMixer:
|
||||
case NodeType::AudioMixer:
|
||||
case NodeType::EntityServer:
|
||||
case NodeType::AssetClient:
|
||||
case NodeType::AssetServer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
|
@ -10,11 +10,31 @@
|
|||
//
|
||||
|
||||
var chatter = SoundCache.getSound("atp:d9978e693035d4e2b5c7b546c8cccfb2dde5677834d9eed5206ccb2da55b4732.wav");
|
||||
var extras = [
|
||||
SoundCache.getSound("atp:1ee58f4d929fdef7c2989cd8be964952a24cdd653d80f57b6a89a2ae8e3029e1.wav"), // zorba
|
||||
SoundCache.getSound("atp:cefaba2d5f1a378382ee046716bffcf6b6a40676649b23a1e81a996efe22d7d3.wav"), // charge
|
||||
SoundCache.getSound("atp:fb41e37f8f8f7b78e546ac78800df6e39edaa09b2df4bfa0afdd8d749dac38b8.wav"), // take me out to the ball game
|
||||
SoundCache.getSound("atp:44a83a788ccfd2924e35c902c34808b24dbd0309d000299ce01a355f91cf8115.wav") // clapping
|
||||
];
|
||||
|
||||
function playChatter() {
|
||||
if (chatter.downloaded && !chatter.isPlaying) {
|
||||
Audio.playSound(chatter, { position: { x: 0, y: 0, z: 0}, loop: true, volume: 0.5 });
|
||||
Audio.playSound(chatter, { loop: true, volume: 0.5 });
|
||||
}
|
||||
}
|
||||
|
||||
chatter.ready.connect(playChatter);
|
||||
|
||||
var currentInjector = null;
|
||||
|
||||
function playRandomExtras() {
|
||||
if ((!currentInjector || !currentInjector.isPlaying) && (Math.random() < (1.0 / 1800.0))) {
|
||||
// play a random extra sound about every 30s
|
||||
currentInjector = Audio.playSound(
|
||||
extras[Math.floor(Math.random() * extras.length)],
|
||||
{ volume: 0.33 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(playRandomExtras);
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
//
|
||||
// baseball.js
|
||||
// examples/toys
|
||||
//
|
||||
// Created by Stephen Birarda on 10/20/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var ROBOT_MODEL = "atp:785c81e117206c36205404beec0cc68529644fe377542dbb2d13fae4d665a5de.fbx";
|
||||
var ROBOT_POSITION = { x: -0.81, y: 0.88, z: 2.12 };
|
||||
var ROBOT_DIMENSIONS = { x: 0.95, y: 1.76, z: 0.56 };
|
||||
|
||||
var BAT_MODEL = "atp:07bdd769a57ff15ebe9331ae4e2c2eae8886a6792b4790cce03b4716eb3a81c7.fbx"
|
||||
var BAT_COLLISION_MODEL = "atp:atp:9eafceb7510c41d50661130090de7e0632aa4da236ebda84a0059a4be2130e0c.obj"
|
||||
var BAT_DIMENSIONS = { x: 1.35, y: 0.10, z: 0.10 };
|
||||
var BAT_REGISTRATION_POINT = { x: 0.1, y: 0.5, z: 0.5 };
|
||||
|
||||
// add the fresh robot at home plate
|
||||
var robot = Entities.addEntity({
|
||||
name: 'Robot',
|
||||
type: 'Model',
|
||||
modelURL: ROBOT_MODEL,
|
||||
position: ROBOT_POSITION,
|
||||
// dimensions: ROBOT_DIMENSIONS,a
|
||||
animationIsPlaying: true,
|
||||
animation: {
|
||||
url: ROBOT_MODEL,
|
||||
fps: 30
|
||||
}
|
||||
});
|
||||
|
||||
// add the bat
|
||||
var bat = Entities.addEntity({
|
||||
name: 'Bat',
|
||||
/*/
|
||||
type: 'Box',
|
||||
/*/
|
||||
type: 'Model',
|
||||
modelURL: BAT_COLLISION_MODEL,
|
||||
/**/
|
||||
collisionModelURL: BAT_COLLISION_MODEL,
|
||||
// dimensions: BAT_DIMENSIONS,
|
||||
registrationPoint: BAT_REGISTRATION_POINT,
|
||||
visible: false
|
||||
})
|
||||
|
||||
var lastTriggerValue = 0.0;
|
||||
|
||||
function checkTriggers() {
|
||||
var rightTrigger = Controller.getTriggerValue(1);
|
||||
|
||||
if (rightTrigger == 0) {
|
||||
if (lastTriggerValue > 0) {
|
||||
// the trigger was just released, play out to the last frame of the swing
|
||||
Entities.editEntity(robot, {
|
||||
animation: {
|
||||
running: true,
|
||||
currentFrame: 21,
|
||||
lastFrame: 115
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (lastTriggerValue == 0) {
|
||||
// the trigger was just depressed, start the swing
|
||||
Entities.editEntity(robot, {
|
||||
animation: {
|
||||
running: true,
|
||||
currentFrame: 0,
|
||||
lastFrame: 21
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
lastTriggerValue = rightTrigger;
|
||||
}
|
||||
|
||||
var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position
|
||||
var ACTION_LIFETIME = 15; // seconds
|
||||
|
||||
var action = null;
|
||||
var factor = 0.0;
|
||||
var STEP = 0.05;
|
||||
function moveBat() {
|
||||
var JOINT_INDEX = 19;
|
||||
|
||||
var forearmPosition = Entities.getJointPosition(robot, JOINT_INDEX);
|
||||
var forearmRotation = Entities.getJointRotation(robot, JOINT_INDEX);
|
||||
|
||||
/*/
|
||||
var properties = Entities.getEntityProperties(bat, ["position", "rotation"]);
|
||||
var offsetPosition = Vec3.subtract(properties.position, forearmPosition);
|
||||
var offsetRotation = Quat.multiply(Quat.inverse(forearmRotation), properties.rotation);
|
||||
print("offsetPosition = " + JSON.stringify(offsetPosition));
|
||||
print("offsetRotation = " + JSON.stringify(Quat.safeEulerAngles(offsetRotation)));
|
||||
/*/
|
||||
Entities.editEntity(bat, {
|
||||
position: forearmPosition,
|
||||
rotation: forearmRotation,
|
||||
});
|
||||
/**/
|
||||
|
||||
// var actionProperties = {
|
||||
// relativePosition: forearmPosition,
|
||||
// relativeRotation: forearmRotation,
|
||||
//// tag: "bat-to-forearm",
|
||||
//// linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
|
||||
//// angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
|
||||
//// lifetime: ACTION_LIFETIME
|
||||
// hand: "left",
|
||||
// timeScale: 0.15
|
||||
// };
|
||||
//
|
||||
// if (action === null) {
|
||||
// Entities.addAction("hold", bat, actionProperties);
|
||||
// } else {
|
||||
// Entities.editAction(bat, action, actionProperties);
|
||||
// }
|
||||
}
|
||||
|
||||
function update() {
|
||||
// checkTriggers();
|
||||
moveBat();
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
Entities.deleteEntity(robot);
|
||||
Entities.deleteEntity(bat);
|
||||
if (action) {
|
||||
Entities.deleteAction(bat, action);
|
||||
}
|
||||
}
|
||||
|
||||
// hook the update so we can check controller triggers
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
|
@ -564,7 +564,7 @@ CameraTool = function(cameraManager) {
|
|||
|
||||
var ORIENTATION_OVERLAY_SIZE = 26;
|
||||
var ORIENTATION_OVERLAY_HALF_SIZE = ORIENTATION_OVERLAY_SIZE / 2;
|
||||
var ORIENTATION_OVERLAY_CUBE_SIZE = 10.5,
|
||||
var ORIENTATION_OVERLAY_CUBE_SIZE = 10.5;
|
||||
|
||||
var ORIENTATION_OVERLAY_OFFSET = {
|
||||
x: 30,
|
||||
|
|
|
@ -6,7 +6,7 @@ SoundArray = function(audioOptions, autoUpdateAudioPosition) {
|
|||
this.audioOptions = audioOptions !== undefined ? audioOptions : {};
|
||||
this.autoUpdateAudioPosition = autoUpdateAudioPosition !== undefined ? autoUpdateAudioPosition : false;
|
||||
if (this.audioOptions.position === undefined) {
|
||||
this.audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0}),
|
||||
this.audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0});
|
||||
}
|
||||
if (this.audioOptions.volume === undefined) {
|
||||
this.audioOptions.volume = 1.0;
|
||||
|
|
|
@ -46,7 +46,7 @@ void AssetResourceRequest::doSend() {
|
|||
}
|
||||
|
||||
connect(_assetRequest, &AssetRequest::progress, this, &AssetResourceRequest::progress);
|
||||
connect(_assetRequest, &AssetRequest::finished, [this](AssetRequest* req) {
|
||||
connect(_assetRequest, &AssetRequest::finished, this, [this](AssetRequest* req) {
|
||||
Q_ASSERT(_state == InProgress);
|
||||
Q_ASSERT(req == _assetRequest);
|
||||
Q_ASSERT(req->getState() == AssetRequest::Finished);
|
||||
|
|
|
@ -79,7 +79,6 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) {
|
|||
QScriptValue inputControllerToScriptValue(QScriptEngine *engine, AbstractInputController* const &in) {
|
||||
return engine->newQObject(in);
|
||||
}
|
||||
|
||||
void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputController* &out) {
|
||||
out = qobject_cast<AbstractInputController*>(object.toQObject());
|
||||
}
|
||||
|
@ -95,9 +94,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
|
|||
_wantSignals(wantSignals),
|
||||
_controllerScriptingInterface(controllerScriptingInterface),
|
||||
_fileNameString(fileNameString),
|
||||
_quatLibrary(),
|
||||
_vec3Library(),
|
||||
_uuidLibrary(),
|
||||
_isUserLoaded(false),
|
||||
_isReloading(false),
|
||||
_arrayBufferClass(new ArrayBufferClass(this))
|
||||
|
@ -513,18 +509,22 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString&
|
|||
});
|
||||
|
||||
// Two common cases of event handler, differing only in argument signature.
|
||||
auto makeSingleEntityHandler = [&](QString eventName) {
|
||||
using SingleEntityHandler = std::function<void(const EntityItemID&)>;
|
||||
auto makeSingleEntityHandler = [this](QString eventName) -> SingleEntityHandler {
|
||||
return [this, eventName](const EntityItemID& entityItemID) {
|
||||
forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this) });
|
||||
};
|
||||
};
|
||||
auto makeMouseHandler = [&](QString eventName) {
|
||||
|
||||
using MouseHandler = std::function<void(const EntityItemID&, const MouseEvent&)>;
|
||||
auto makeMouseHandler = [this](QString eventName) -> MouseHandler {
|
||||
return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) {
|
||||
forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) });
|
||||
};
|
||||
};
|
||||
|
||||
auto makeCollisionHandler = [&](QString eventName) {
|
||||
using CollisionHandler = std::function<void(const EntityItemID&, const EntityItemID&, const Collision&)>;
|
||||
auto makeCollisionHandler = [this](QString eventName) -> CollisionHandler {
|
||||
return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) {
|
||||
forwardHandlerCall(idA, eventName, { idA.toScriptValue(this), idB.toScriptValue(this),
|
||||
collisionToScriptValue(this, collision) });
|
||||
|
@ -556,7 +556,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString&
|
|||
}
|
||||
|
||||
|
||||
QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {
|
||||
QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) {
|
||||
if (_stoppingAllScripts) {
|
||||
return QScriptValue(); // bail early
|
||||
}
|
||||
|
@ -565,27 +565,30 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN
|
|||
QScriptValue result;
|
||||
#ifdef THREAD_DEBUGGING
|
||||
qDebug() << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||
"program:" << program << " fileName:" << fileName << "lineNumber:" << lineNumber;
|
||||
"sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber;
|
||||
#endif
|
||||
QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QScriptValue, result),
|
||||
Q_ARG(const QString&, program),
|
||||
Q_ARG(const QString&, sourceCode),
|
||||
Q_ARG(const QString&, fileName),
|
||||
Q_ARG(int, lineNumber));
|
||||
return result;
|
||||
}
|
||||
|
||||
_evaluatesPending++;
|
||||
QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber);
|
||||
if (hasUncaughtException()) {
|
||||
int line = uncaughtExceptionLineNumber();
|
||||
qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString();
|
||||
// Check syntax
|
||||
const QScriptProgram program(sourceCode, fileName, lineNumber);
|
||||
if (!checkSyntax(program)) {
|
||||
return QScriptValue();
|
||||
}
|
||||
_evaluatesPending--;
|
||||
|
||||
++_evaluatesPending;
|
||||
const auto result = QScriptEngine::evaluate(program);
|
||||
--_evaluatesPending;
|
||||
|
||||
const auto hadUncaughtException = checkExceptions(*this, program.fileName());
|
||||
if (_wantSignals) {
|
||||
emit evaluationFinished(result, hasUncaughtException());
|
||||
emit evaluationFinished(result, hadUncaughtException);
|
||||
}
|
||||
clearExceptions();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -603,7 +606,7 @@ void ScriptEngine::run() {
|
|||
emit runningStateChanged();
|
||||
}
|
||||
|
||||
QScriptValue result = evaluate(_scriptContents);
|
||||
QScriptValue result = evaluate(_scriptContents, _fileNameString);
|
||||
|
||||
QElapsedTimer startTime;
|
||||
startTime.start();
|
||||
|
@ -644,15 +647,6 @@ void ScriptEngine::run() {
|
|||
qint64 now = usecTimestampNow();
|
||||
float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND;
|
||||
|
||||
if (hasUncaughtException()) {
|
||||
int line = uncaughtExceptionLineNumber();
|
||||
qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << uncaughtException().toString();
|
||||
if (_wantSignals) {
|
||||
emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + uncaughtException().toString());
|
||||
}
|
||||
clearExceptions();
|
||||
}
|
||||
|
||||
if (!_isFinished) {
|
||||
if (_wantSignals) {
|
||||
emit update(deltaTime);
|
||||
|
@ -660,6 +654,9 @@ void ScriptEngine::run() {
|
|||
}
|
||||
lastUpdate = now;
|
||||
|
||||
if (!checkExceptions(*this, _fileNameString)) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
stopAllTimers(); // make sure all our timers are stopped if the script is ending
|
||||
|
@ -896,6 +893,37 @@ void ScriptEngine::load(const QString& loadFile) {
|
|||
}
|
||||
}
|
||||
|
||||
bool ScriptEngine::checkSyntax(const QScriptProgram& program) {
|
||||
const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());
|
||||
if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
|
||||
const auto error = syntaxCheck.errorMessage();
|
||||
const auto line = QString::number(syntaxCheck.errorLineNumber());
|
||||
const auto column = QString::number(syntaxCheck.errorColumnNumber());
|
||||
const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column);
|
||||
qCWarning(scriptengine) << qPrintable(message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptEngine::checkExceptions(QScriptEngine& engine, const QString& fileName) {
|
||||
if (engine.hasUncaughtException()) {
|
||||
const auto backtrace = engine.uncaughtExceptionBacktrace();
|
||||
const auto exception = engine.uncaughtException().toString();
|
||||
const auto line = QString::number(engine.uncaughtExceptionLineNumber());
|
||||
engine.clearExceptions();
|
||||
|
||||
auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line);
|
||||
if (!backtrace.empty()) {
|
||||
static const auto lineSeparator = "\n ";
|
||||
message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
|
||||
}
|
||||
qCWarning(scriptengine) << qPrintable(message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
|
||||
void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
|
@ -978,14 +1006,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
|
||||
auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript");
|
||||
|
||||
// first check the syntax of the script contents
|
||||
QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(contents);
|
||||
if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
|
||||
qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID;
|
||||
qCDebug(scriptengine) << " " << syntaxCheck.errorMessage() << ":"
|
||||
<< syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber();
|
||||
qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
|
||||
QScriptProgram program(contents, fileName);
|
||||
if (!checkSyntax(program)) {
|
||||
if (!isFileUrl) {
|
||||
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
||||
}
|
||||
|
@ -997,12 +1021,15 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
}
|
||||
|
||||
QScriptEngine sandbox;
|
||||
QScriptValue testConstructor = sandbox.evaluate(contents);
|
||||
QScriptValue testConstructor = sandbox.evaluate(program);
|
||||
if (!checkExceptions(sandbox, program.fileName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!testConstructor.isFunction()) {
|
||||
qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID;
|
||||
qCDebug(scriptengine) << " NOT CONSTRUCTOR";
|
||||
qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
|
||||
qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n"
|
||||
" NOT CONSTRUCTOR\n"
|
||||
" SCRIPT:" << scriptOrURL;
|
||||
if (!isFileUrl) {
|
||||
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
||||
}
|
||||
|
@ -1015,7 +1042,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
QScriptValue entityScriptConstructor = evaluate(contents);
|
||||
QScriptValue entityScriptConstructor = evaluate(contents, fileName);
|
||||
QScriptValue entityScriptObject = entityScriptConstructor.construct();
|
||||
EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified };
|
||||
_entityScripts[entityID] = newDetails;
|
||||
|
|
|
@ -88,7 +88,7 @@ public:
|
|||
Q_INVOKABLE void registerValue(const QString& valueName, QScriptValue value);
|
||||
|
||||
/// evaluate some code in the context of the ScriptEngine and return the result
|
||||
Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName = QString(), int lineNumber = 1); // this is also used by the script tool widget
|
||||
Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName, int lineNumber = 1); // this is also used by the script tool widget
|
||||
|
||||
/// if the script engine is not already running, this will download the URL and start the process of seting it up
|
||||
/// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed
|
||||
|
@ -182,6 +182,9 @@ private:
|
|||
QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot);
|
||||
void stopTimer(QTimer* timer);
|
||||
|
||||
static bool checkSyntax(const QScriptProgram& program);
|
||||
static bool checkExceptions(QScriptEngine& engine, const QString& fileName);
|
||||
|
||||
AbstractControllerScriptingInterface* _controllerScriptingInterface;
|
||||
QString _fileNameString;
|
||||
Quat _quatLibrary;
|
||||
|
|
|
@ -135,7 +135,7 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont
|
|||
prefixString.append(QString(" [%1]").arg(_targetName));
|
||||
}
|
||||
|
||||
QString logMessage = QString("%1 %2").arg(prefixString, message);
|
||||
QString logMessage = QString("%1 %2").arg(prefixString, message.split("\n").join("\n" + prefixString + " "));
|
||||
fprintf(stdout, "%s\n", qPrintable(logMessage));
|
||||
return logMessage;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue