Пример #1
0
/*
 * Give @unit and its cargo to @recipient.
 * No action if @recipient already owns @unit.
 * If @giver is non-zero, inform @recipient and @giver of the transaction.
 * Clears mission and group on the units given away.
 */
void
unit_give_away(struct empobj *unit, natid recipient, natid giver)
{
    int type;
    struct nstr_item ni;
    union empobj_storage cargo;

    if (unit->own == recipient)
	return;

    if (giver) {
	mpr(unit->own, "%s given to %s\n",
	    unit_nameof(unit), cname(recipient));
	mpr(recipient, "%s given to you by %s\n",
	    unit_nameof(unit), cname(giver));
    }

    unit->own = recipient;
    unit_wipe_orders(unit);
    put_empobj(unit->ef_type, unit->uid, unit);

    for (type = EF_PLANE; type <= EF_NUKE; type++) {
	snxtitem_cargo(&ni, type, unit->ef_type, unit->uid);
	while (nxtitem(&ni, &cargo))
	    unit_give_away(&cargo.gen, recipient, giver);
    }
}
Пример #2
0
void
takeover_plane(struct plnstr *pp, natid newown)
{
    int n;

    if ((pp->pln_own == newown) || (pp->pln_own == 0))
	return;
    if (pp->pln_flags & PLN_LAUNCHED)
	return;
    if (pp->pln_ship >= 0 || pp->pln_land >= 0)
	return;
    /*
     * XXX If this was done right, planes could escape,
     * flying to a nearby friendly airport.
     */
    n = pp->pln_effic - (29 + roll(100));
    if (n < 0)
	n = 0;
    pp->pln_effic = n;
    if (pp->pln_effic < PLANE_MINEFF || pp->pln_harden > 0) {
	pp->pln_effic = 0;
	mpr(newown, "%s blown up by the crew!\n", prplane(pp));
	wu(0, pp->pln_own,
	   "%s blown up by the crew to avoid capture by %s at %s!\n",
	   prplane(pp),
	   cname(newown), xyas(pp->pln_x, pp->pln_y, pp->pln_own));
    } else {
	mpr(newown, "We have captured %s!\n", prplane(pp));
	wu(0, pp->pln_own,
	   "%s captured by %s at %s!\n",
	   prplane(pp),
	   cname(newown), xyas(pp->pln_x, pp->pln_y, pp->pln_own));
    }
    takeover_unit((struct empobj *)pp, newown);
}
Пример #3
0
int
msl_hit(struct plnstr *pp, int hardtarget, int type,
	int news_item, int snews_item, int sublaunch, natid victim)
{
    int hitchance, hit;

    if (nuk_on_plane(pp) >= 0) {
	mpr(pp->pln_own, "\tArming nuclear warheads...\n");
	hit = 1;
    } else {
	hitchance = pln_hitchance(pp, hardtarget, type);
	hit = pct_chance(hitchance);
	mpr(pp->pln_own, "\t%d%% hitchance...%s\n", hitchance,
	    hit ? "HIT!" : "miss");
    }

    if (type != EF_PLANE)
	mpr(victim, "...Incoming %s missile %s\n",
	    sublaunch ? "" : cname(pp->pln_own),
	    hit ? "HIT!\n" : "missed\n");
    if (hit && news_item) {
	if (sublaunch)
	    nreport(victim, snews_item, 0, 1);
	else
	    nreport(pp->pln_own, news_item, victim, 1);
    }
    return hit;
}
Пример #4
0
/*
 * Drop cargo of @unit.
 * Give it to @newown, unless it's zero.
 */
void
unit_drop_cargo(struct empobj *unit, natid newown)
{
    int type;
    struct nstr_item ni;
    union empobj_storage cargo;

    for (type = EF_PLANE; type <= EF_NUKE; type++) {
	snxtitem_cargo(&ni, type, unit->ef_type, unit->uid);
	while (nxtitem(&ni, &cargo)) {
	    switch (type) {
	    case EF_PLANE:
		cargo.plane.pln_ship = cargo.plane.pln_land = -1;
		break;
	    case EF_LAND:
		cargo.land.lnd_ship = cargo.land.lnd_land = -1;
		break;
	    case EF_NUKE:
		cargo.nuke.nuk_plane = -1;
		break;
	    }
	    mpr(cargo.gen.own, "%s transferred off %s %d to %s\n",
		unit_nameof(&cargo.gen),
		ef_nameof(unit->ef_type), unit->uid,
		xyas(cargo.gen.x, cargo.gen.y, cargo.gen.own));
	    if (newown)
		unit_give_away(&cargo.gen, newown, cargo.gen.own);
	    put_empobj(type, cargo.gen.uid, &cargo.gen);
	}
    }
}
Пример #5
0
void
pln_put1(struct plist *plp)
{
    struct plnstr *pp;
    struct shpstr ship;
    struct sctstr sect;

    pp = &plp->plane;

    if (CANT_HAPPEN((pp->pln_flags & PLN_LAUNCHED)
		    && (plchr[pp->pln_type].pl_flags & P_M)
		    && pp->pln_effic >= PLANE_MINEFF))
	pp->pln_effic = 0;   /* bug: missile launched but not used up */

    if (!(pp->pln_flags & PLN_LAUNCHED))
	;			/* never took off */
    else if (pp->pln_effic < PLANE_MINEFF) {
	;			/* destroyed */
    } else if (pp->pln_ship >= 0) {
	/* It is landing on a carrier */
	getship(pp->pln_ship, &ship);
	/* We should do more, like make sure it's really
	   a carrier, etc. but for now just make sure it's
	   not sunk. */
	if (ship.shp_effic < SHIP_MINEFF) {
	    mpr(pp->pln_own,
		"Ship #%d has been sunk, plane #%d has nowhere to land, and\n"
		"splashes into the sea.\n",
		pp->pln_ship, pp->pln_uid);
	    pp->pln_effic = 0;
	}
    } else {
	/* Presume we are landing back in a sector. */
	getsect(pp->pln_x, pp->pln_y, &sect);
	if (sect.sct_type == SCT_WATER || sect.sct_type == SCT_WASTE) {
	    mpr(pp->pln_own,
		"Nowhere to land at %s, plane #%d crashes and burns...\n",
		xyas(pp->pln_x, pp->pln_y, pp->pln_own), pp->pln_uid);
	    pp->pln_effic = 0;
	}
    }
    pp->pln_flags &= ~PLN_LAUNCHED;
    putplane(pp->pln_uid, pp);
    emp_remque(&plp->queue);
    free(plp);
}
Пример #6
0
    /**
     * For a given query, get a runner.  The runner could be a SingleSolutionRunner, a
     * CachedQueryRunner, or a MultiPlanRunner, depending on the cache/query solver/etc.
     */
    Status getRunner(QueryMessage& q, Runner** out) {
        CanonicalQuery* rawCanonicalQuery = NULL;

        // Canonicalize the query and wrap it in an auto_ptr so we don't leak it if something goes
        // wrong.
        Status status = CanonicalQuery::canonicalize(q, &rawCanonicalQuery);
        if (!status.isOK()) { return status; }
        verify(rawCanonicalQuery);
        auto_ptr<CanonicalQuery> canonicalQuery(rawCanonicalQuery);

        // Try to look up a cached solution for the query.
        // TODO: Can the cache have negative data about a solution?
        PlanCache* localCache = PlanCache::get(canonicalQuery->ns());
        CachedSolution* cs = localCache->get(*canonicalQuery);
        if (NULL != cs) {
            // We have a cached solution.  Hand the canonical query and cached solution off to the
            // cached plan runner, which takes ownership of both.
            WorkingSet* ws;
            PlanStage* root;
            verify(StageBuilder::build(*cs->solution, &root, &ws));
            *out = new CachedPlanRunner(canonicalQuery.release(), cs, root, ws);
            return Status::OK();
        }

        // No entry in cache for the query.  We have to solve the query ourself.
        vector<QuerySolution*> solutions;
        QueryPlanner::plan(*canonicalQuery, &solutions);

        // We cannot figure out how to answer the query.  Should this ever happen?
        if (0 == solutions.size()) {
            return Status(ErrorCodes::BadValue, "Can't create a plan for the canonical query " +
                                                 canonicalQuery->toString());
        }

        if (1 == solutions.size()) {
            // Only one possible plan.  Run it.  Build the stages from the solution.
            WorkingSet* ws;
            PlanStage* root;
            verify(StageBuilder::build(*solutions[0], &root, &ws));

            // And, run the plan.
            *out = new SingleSolutionRunner(canonicalQuery.release(), solutions[0], root, ws);
            return Status::OK();
        }
        else {
            // Many solutions.  Let the MultiPlanRunner pick the best, update the cache, and so on.
            auto_ptr<MultiPlanRunner> mpr(new MultiPlanRunner(canonicalQuery.release()));
            for (size_t i = 0; i < solutions.size(); ++i) {
                WorkingSet* ws;
                PlanStage* root;
                verify(StageBuilder::build(*solutions[i], &root, &ws));
                // Takes ownership of all arguments.
                mpr->addPlan(solutions[i], root, ws);
            }
            *out = mpr.release();
            return Status::OK();
        }
    }
void QwtPlot::print(QPaintDevice &paintDev,
   const QwtPlotPrintFilter &pfilter) const
{
    QPaintDeviceMetrics mpr(&paintDev);

    QRect rect(0, 0, mpr.width(), mpr.height());
    double aspect = double(rect.width())/double(rect.height());
    if ((aspect < 1.0))
        rect.setHeight(int(aspect*rect.width()));

    QPainter p(&paintDev);
    print(&p, rect, pfilter);
}
Пример #8
0
int
msl_asat_intercept(struct plnstr *msl, coord x, coord y)
{
    struct sctstr sect;
    struct emp_qelem irvlist;

    getsect(x, y, &sect);
    mpr(sect.sct_own, "%s has positioned a satellite over %s\n",
	cname(msl->pln_own), xyas(x, y, sect.sct_own));
    msl_sel(&irvlist, x, y, msl->pln_own, P_O, 0, 0);
    return msl_intercept(msl, &sect, 0,
			 &irvlist, "satellite", "a-sat missile",
			 N_SAT_KILL);
}
Пример #9
0
TEST(MPRReaderV4, readMPR) {
	std::istringstream text (
		"version4\n"
		"mikroprogramm:\n"
		"000 IFETCH 3b 00 78 60 60 00 82 00 00 84\n"
		"001 3d 00 78 60 60 00 82 00 00 84\n"
		"maschinenprogramm:\n"
		"0000-03ff\n"
		"0104\n"
		"0115"
	);

	std::ostream cerr(0);

	MPRReaderV4 reader;
	MPRFile mpr("test file 1");

	reader.readMPR(text, mpr, cerr);

	// check size of vectors
	ASSERT_EQ(2, mpr.getMicroLines().size());
	ASSERT_EQ(2, mpr.getRamCells().size());

	// check file name
	EXPECT_EQ("test file 1", mpr.getName());

	// check first micro line
	std::bitset<80> bits;
	bits[0] = 1;
	bits[1] = 1; bits[3] = 1; bits[4] = 1; bits[5] = 1;
	bits[19] = 1; bits[20] = 1; bits[21] = 1; bits[22] = 1;
	bits[29] = 1; bits[30] = 1;
	bits[37] = 1; bits[38] = 1;
	bits[49] = 1;
	bits[55] = 1;
	bits[74] = 1;
	bits[79] = 1;

	EXPECT_EQ(bits, mpr.getMicroLines()[0].getBits());

	// check micro line line numbers
	EXPECT_EQ(0, mpr.getMicroLines()[0].getLineNumber());
	EXPECT_EQ(1, mpr.getMicroLines()[1].getLineNumber());

	// check ram cells
	EXPECT_EQ("0104", mpr.getRamCells()[0].getData());
	EXPECT_EQ("0115", mpr.getRamCells()[1].getData());
}
Пример #10
0
/*!
   \brief Render the plot to a paint device ( f.e a QPrinter )

   A convenience method, that calculates the target rectangle
   from the paintdevice metrics.

   \sa renderTo(QPainter *, const QRect &)
*/
void QwtPolarPlot::renderTo( QPaintDevice &paintDev ) const
{
#if QT_VERSION < 0x040000
  QPaintDeviceMetrics mpr( &paintDev );
  int w = mpr.width();
  int h = mpr.height();
#else
  int w = paintDev.width();
  int h = paintDev.height();
#endif

  const QRect rect( 0, 0, w, h );

  QPainter p( &paintDev );
  renderTo( &p, rect );

}
Пример #11
0
TEST(MPRReaderV4, readMPRWithWrongContent) {
	std::istringstream text (
		"<html>\n"
		"<head></head>"
	);

	std::ostringstream errStream;

	MPRReaderV4 reader;
	MPRFile mpr("with wrong content");

	bool returnCode = reader.readMPR(text, mpr, errStream);

	EXPECT_FALSE(returnCode) << "Should return false in case of parsing error";
	EXPECT_NE("", errStream.str()) << "Should print error to error stream";

	EXPECT_EQ(0, mpr.getMicroLines().size());
	EXPECT_EQ(0, mpr.getRamCells().size());
}
Пример #12
0
void QwtPlot::print(QPaintDevice &paintDev,
                    const QwtPlotPrintFilter &pfilter) const
{
#if QT_VERSION < 0x040000
    QPaintDeviceMetrics mpr(&paintDev);
    int w = mpr.width();
    int h = mpr.height();
#else
    int w = paintDev.width();
    int h = paintDev.height();
#endif

    QRect rect(0, 0, w, h);
    double aspect = double(rect.width())/double(rect.height());
    if ((aspect < 1.0))
        rect.setHeight(int(aspect*rect.width()));

    QPainter p(&paintDev);
    print(&p, rect, pfilter);
}
Пример #13
0
/*
 * Update cargo of @carrier for movement or destruction.
 * If the carrier is destroyed, destroy its cargo (planes, land units,
 * nukes).
 * Else update their location to the carrier's.  Any op sectors equal
 * to location get updated, too.
 * Return number of units updated.
 */
int
unit_update_cargo(struct empobj *carrier)
{
    int cargo_type;
    struct nstr_item ni;
    union empobj_storage obj;
    int n = 0;

    for (cargo_type = EF_PLANE; cargo_type <= EF_NUKE; cargo_type++) {
	snxtitem_cargo(&ni, cargo_type, carrier->ef_type, carrier->uid);
	while (nxtitem(&ni, &obj)) {
	    if (carrier->own)
		unit_teleport(&obj.gen, carrier->x, carrier->y);
	    else {
		mpr(obj.gen.own, "%s lost!\n", unit_nameof(&obj.gen));
		obj.gen.effic = 0;
	    }
	    put_empobj(cargo_type, obj.gen.uid, &obj);
	    n++;
	}
    }
    return n;
}
Пример #14
0
    /**
     * For a given query, get a runner.  The runner could be a SingleSolutionRunner, a
     * CachedQueryRunner, or a MultiPlanRunner, depending on the cache/query solver/etc.
     */
    Status getRunner(Collection* collection,
                     CanonicalQuery* rawCanonicalQuery,
                     Runner** out,
                     size_t plannerOptions) {

        verify(rawCanonicalQuery);
        auto_ptr<CanonicalQuery> canonicalQuery(rawCanonicalQuery);

        // This can happen as we're called by internal clients as well.
        if (NULL == collection) {
            const string& ns = canonicalQuery->ns();
            *out = new EOFRunner(canonicalQuery.release(), ns);
            return Status::OK();
        }

        // If we have an _id index we can use the idhack runner.
        if (canUseIDHack(*canonicalQuery) && collection->getIndexCatalog()->findIdIndex()) {
            *out = new IDHackRunner(collection, canonicalQuery.release());
            return Status::OK();
        }

        // If it's not NULL, we may have indices.  Access the catalog and fill out IndexEntry(s)
        QueryPlannerParams plannerParams;

        IndexCatalog::IndexIterator ii = collection->getIndexCatalog()->getIndexIterator(false);
        while (ii.more()) {
            const IndexDescriptor* desc = ii.next();
            plannerParams.indices.push_back(IndexEntry(desc->keyPattern(),
                                                       desc->isMultikey(),
                                                       desc->isSparse(),
                                                       desc->indexName(),
                                                       desc->infoObj()));
        }

        // If query supports index filters, filter params.indices by indices in query settings.
        QuerySettings* querySettings = collection->infoCache()->getQuerySettings();
        AllowedIndices* allowedIndicesRaw;

        // Filter index catalog if index filters are specified for query.
        // Also, signal to planner that application hint should be ignored.
        if (querySettings->getAllowedIndices(*canonicalQuery, &allowedIndicesRaw)) {
            boost::scoped_ptr<AllowedIndices> allowedIndices(allowedIndicesRaw);
            filterAllowedIndexEntries(*allowedIndices, &plannerParams.indices);
            plannerParams.indexFiltersApplied = true;
        }

        // Tailable: If the query requests tailable the collection must be capped.
        if (canonicalQuery->getParsed().hasOption(QueryOption_CursorTailable)) {
            if (!collection->isCapped()) {
                return Status(ErrorCodes::BadValue,
                              "error processing query: " + canonicalQuery->toString() +
                              " tailable cursor requested on non capped collection");
            }

            // If a sort is specified it must be equal to expectedSort.
            const BSONObj expectedSort = BSON("$natural" << 1);
            const BSONObj& actualSort = canonicalQuery->getParsed().getSort();
            if (!actualSort.isEmpty() && !(actualSort == expectedSort)) {
                return Status(ErrorCodes::BadValue,
                              "error processing query: " + canonicalQuery->toString() +
                              " invalid sort specified for tailable cursor: "
                              + actualSort.toString());
            }
        }

        // Process the planning options.
        plannerParams.options = plannerOptions;
        if (storageGlobalParams.noTableScan) {
            const string& ns = canonicalQuery->ns();
            // There are certain cases where we ignore this restriction:
            bool ignore = canonicalQuery->getQueryObj().isEmpty()
                          || (string::npos != ns.find(".system."))
                          || (0 == ns.find("local."));
            if (!ignore) {
                plannerParams.options |= QueryPlannerParams::NO_TABLE_SCAN;
            }
        }

        if (!(plannerParams.options & QueryPlannerParams::NO_TABLE_SCAN)) {
            plannerParams.options |= QueryPlannerParams::INCLUDE_COLLSCAN;
        }

        // If the caller wants a shard filter, make sure we're actually sharded.
        if (plannerParams.options & QueryPlannerParams::INCLUDE_SHARD_FILTER) {
            CollectionMetadataPtr collMetadata =
                shardingState.getCollectionMetadata(canonicalQuery->ns());

            if (collMetadata) {
                plannerParams.shardKey = collMetadata->getKeyPattern();
            }
            else {
                // If there's no metadata don't bother w/the shard filter since we won't know what
                // the key pattern is anyway...
                plannerParams.options &= ~QueryPlannerParams::INCLUDE_SHARD_FILTER;
            }
        }

        // Try to look up a cached solution for the query.
        //
        // Skip cache look up for non-cacheable queries.
        // See PlanCache::shouldCacheQuery()
        //
        // TODO: Can the cache have negative data about a solution?
        CachedSolution* rawCS;
        if (PlanCache::shouldCacheQuery(*canonicalQuery) &&
            collection->infoCache()->getPlanCache()->get(*canonicalQuery, &rawCS).isOK()) {
            // We have a CachedSolution.  Have the planner turn it into a QuerySolution.
            boost::scoped_ptr<CachedSolution> cs(rawCS);
            QuerySolution *qs, *backupQs;
            Status status = QueryPlanner::planFromCache(*canonicalQuery, plannerParams, *cs,
                                                        &qs, &backupQs);

            // See SERVER-12438. Unfortunately we have to defer to the backup solution
            // if both a batch size is set and a sort is requested.
            //
            // TODO: it would be really nice to delete this block in the future.
            if (status.isOK() && NULL != backupQs &&
                0 < canonicalQuery->getParsed().getNumToReturn() &&
                !canonicalQuery->getParsed().getSort().isEmpty()) {
                delete qs;

                WorkingSet* ws;
                PlanStage* root;
                verify(StageBuilder::build(*backupQs, &root, &ws));

                // And, run the plan.
                *out = new SingleSolutionRunner(collection,
                                                canonicalQuery.release(),
                                                backupQs, root, ws);
                return Status::OK();
            }

            if (status.isOK()) {
                if (plannerParams.options & QueryPlannerParams::PRIVATE_IS_COUNT) {
                    if (turnIxscanIntoCount(qs)) {
                        WorkingSet* ws;
                        PlanStage* root;
                        verify(StageBuilder::build(*qs, &root, &ws));
                        *out = new SingleSolutionRunner(collection,
                                                        canonicalQuery.release(), qs, root, ws);
                        if (NULL != backupQs) {
                            delete backupQs;
                        }
                        return Status::OK();
                    }
                }

                WorkingSet* ws;
                PlanStage* root;
                verify(StageBuilder::build(*qs, &root, &ws));
                CachedPlanRunner* cpr = new CachedPlanRunner(collection,
                                                             canonicalQuery.release(), qs,
                                                             root, ws);

                if (NULL != backupQs) {
                    WorkingSet* backupWs;
                    PlanStage* backupRoot;
                    verify(StageBuilder::build(*backupQs, &backupRoot, &backupWs));
                    cpr->setBackupPlan(backupQs, backupRoot, backupWs);
                }

                *out = cpr;
                return Status::OK();
            }
        }

        if (enableIndexIntersection) {
            plannerParams.options |= QueryPlannerParams::INDEX_INTERSECTION;
        }

        plannerParams.options |= QueryPlannerParams::KEEP_MUTATIONS;

        vector<QuerySolution*> solutions;
        Status status = QueryPlanner::plan(*canonicalQuery, plannerParams, &solutions);
        if (!status.isOK()) {
            return Status(ErrorCodes::BadValue,
                          "error processing query: " + canonicalQuery->toString() +
                          " planner returned error: " + status.reason());
        }

        // We cannot figure out how to answer the query.  Perhaps it requires an index
        // we do not have?
        if (0 == solutions.size()) {
            return Status(ErrorCodes::BadValue,
                          str::stream()
                          << "error processing query: "
                          << canonicalQuery->toString()
                          << " No query solutions");
        }

        // See if one of our solutions is a fast count hack in disguise.
        if (plannerParams.options & QueryPlannerParams::PRIVATE_IS_COUNT) {
            for (size_t i = 0; i < solutions.size(); ++i) {
                if (turnIxscanIntoCount(solutions[i])) {
                    // Great, we can use solutions[i].  Clean up the other QuerySolution(s).
                    for (size_t j = 0; j < solutions.size(); ++j) {
                        if (j != i) {
                            delete solutions[j];
                        }
                    }

                    // We're not going to cache anything that's fast count.
                    WorkingSet* ws;
                    PlanStage* root;
                    verify(StageBuilder::build(*solutions[i], &root, &ws));
                    *out = new SingleSolutionRunner(collection,
                                                    canonicalQuery.release(),
                                                    solutions[i],
                                                    root,
                                                    ws);
                    return Status::OK();
                }
            }
        }

        if (1 == solutions.size()) {
            // Only one possible plan.  Run it.  Build the stages from the solution.
            WorkingSet* ws;
            PlanStage* root;
            verify(StageBuilder::build(*solutions[0], &root, &ws));

            // And, run the plan.
            *out = new SingleSolutionRunner(collection,
                                            canonicalQuery.release(),solutions[0], root, ws);
            return Status::OK();
        }
        else {
            // See SERVER-12438. In an ideal world we should not arbitrarily prefer certain
            // solutions over others. But unfortunately for historical reasons we are forced
            // to prefer a solution where the index provides the sort, if the batch size
            // is set and a sort is requested. Read SERVER-12438 for details, if you dare.
            //
            // TODO: it would be really nice to delete this entire block in the future.
            if (0 < canonicalQuery->getParsed().getNumToReturn()
                && !canonicalQuery->getParsed().getSort().isEmpty()) {
                // Look for a solution without a blocking sort stage.
                for (size_t i = 0; i < solutions.size(); ++i) {
                    if (!solutions[i]->hasSortStage) {
                        WorkingSet* ws;
                        PlanStage* root;
                        verify(StageBuilder::build(*solutions[i], &root, &ws));

                        // Free unused solutions.
                        for (size_t j = 0; j < solutions.size(); ++j) {
                            if (j != i) {
                                delete solutions[j];
                            }
                        }

                        // And, run the plan.
                        *out = new SingleSolutionRunner(collection,
                                                       canonicalQuery.release(),
                                                       solutions[i], root, ws);
                        return Status::OK();
                    }
                }
            }

            // Many solutions.  Let the MultiPlanRunner pick the best, update the cache, and so on.
            auto_ptr<MultiPlanRunner> mpr(new MultiPlanRunner(collection,canonicalQuery.release()));

            for (size_t i = 0; i < solutions.size(); ++i) {
                WorkingSet* ws;
                PlanStage* root;
                if (solutions[i]->cacheData.get()) {
                    solutions[i]->cacheData->indexFilterApplied = plannerParams.indexFiltersApplied;
                }
                verify(StageBuilder::build(*solutions[i], &root, &ws));
                // Takes ownership of all arguments.
                mpr->addPlan(solutions[i], root, ws);
            }
            *out = mpr.release();
            return Status::OK();
        }
    }
Пример #15
0
        void run() {
            Client::WriteContext ctx(ns());

            const int N = 5000;
            for (int i = 0; i < N; ++i) {
                insert(BSON("foo" << (i % 10)));
            }

            addIndex(BSON("foo" << 1));

            // Plan 0: IXScan over foo == 7
            // Every call to work() returns something so this should clearly win (by current scoring
            // at least).
            IndexScanParams ixparams;
            ixparams.descriptor = getIndex(BSON("foo" << 1));
            ixparams.bounds.isSimpleRange = true;
            ixparams.bounds.startKey = BSON("" << 7);
            ixparams.bounds.endKey = BSON("" << 7);
            ixparams.bounds.endKeyInclusive = true;
            ixparams.direction = 1;
            auto_ptr<WorkingSet> firstWs(new WorkingSet());
            IndexScan* ix = new IndexScan(ixparams, firstWs.get(), NULL);
            auto_ptr<PlanStage> firstRoot(new FetchStage(firstWs.get(), ix, NULL));

            // Plan 1: CollScan with matcher.
            CollectionScanParams csparams;
            csparams.ns = ns();
            csparams.direction = CollectionScanParams::FORWARD;
            auto_ptr<WorkingSet> secondWs(new WorkingSet());
            // Make the filter.
            BSONObj filterObj = BSON("foo" << 7);
            StatusWithMatchExpression swme = MatchExpressionParser::parse(filterObj);
            verify(swme.isOK());
            auto_ptr<MatchExpression> filter(swme.getValue());
            // Make the stage.
            auto_ptr<PlanStage> secondRoot(new CollectionScan(csparams, secondWs.get(),
                                                              filter.get()));

            // Hand the plans off to the runner.
            CanonicalQuery* cq = NULL;
            verify(CanonicalQuery::canonicalize(ns(), BSON("foo" << 7), &cq).isOK());
            verify(NULL != cq);
            MultiPlanRunner mpr(ctx.ctx().db()->getCollection(ns()),cq);
            mpr.addPlan(createQuerySolution(), firstRoot.release(), firstWs.release());
            mpr.addPlan(createQuerySolution(), secondRoot.release(), secondWs.release());

            // Plan 0 aka the first plan aka the index scan should be the best.
            size_t best;
            ASSERT(mpr.pickBestPlan(&best));
            ASSERT_EQUALS(size_t(0), best);

            // Get all our results out.
            int results = 0;
            BSONObj obj;
            while (Runner::RUNNER_ADVANCED == mpr.getNext(&obj, NULL)) {
                ASSERT_EQUALS(obj["foo"].numberInt(), 7);
                ++results;
            }

            ASSERT_EQUALS(results, N / 10);
        }
Пример #16
0
int APIENTRY WinMain(HINSTANCE inst, HINSTANCE junk, char *args, 
	int junk2)
{
	MaHttp		*http;					// Http service inside our app
	MaServer	*server;				// For the HTTP server
	Mpr			mpr("winEventLoop");	// Initialize the run time

	//
	//	Do the following two statements only if you want debug trace
	//
	mp = &mpr;

#if BLD_FEATURE_LOG
	mpr.addListener(new MprLogToFile());
	mpr.setLogSpec("error.log:4");
#endif

	if (findInstance()) {
		mprError(MPR_L, MPR_LOG, "Application is already active");
		return FALSE;
	}

	//
	//	Create the window object
	// 
	if (initWindow() < 0) {
		mprError(MPR_L, MPR_ERROR, 
			"Can't initialize application Window");
		return FALSE;
	}

	//
	//	Use windows async select and message dispatcher rather than 
	//	select()
	//
	mp->setAsyncSelectMode(MPR_ASYNC_SELECT);

	//
	//	Start the Embedthis Portable Runtime and request single threading
	//	NOTE: this program can be compiled multi-threaded. If so, change
	//	the parameter to a "1".
	//
	mpr.start();

	//
	//	Create Http and Server objects for this application. We set the
	//	ServerName to be "default" and the initial serverRoot to be ".".
	//	This will be overridden in winEventLoop.conf.
	//
	http = new MaHttp();
	server = new MaServer(http, "default", ".");
	
	//
	//	Activate the copy module and handler
	//
	new MaCopyModule(0);

	//
	//	Configure the server with the configuration directive file
	//
	if (server->configure("winEventLoop.conf") < 0) {
		mprFprintf(MPR_STDERR, 
			"Can't configure the server. Error on line %d\n", 
			server->getLine());
		exit(2);
	}
	
	//
	//	Start the server
	//
	if (http->start() < 0) {
		mprFprintf(MPR_STDERR, "Can't start the server\n");
		exit(2);
	}

	//
	//	Service incoming requests until time to exit.
	//
	eventLoop();

	//
	//	Orderly shutdown
	//
	http->stop();
	mpr.stop(0);
	delete server;
	delete http;

	//
	//	MPR run-time will automatically stop and be cleaned up
	//
	return 0;
}
Пример #17
0
int main(int argc, char** argv)
{
	MaHttp		*http;					// Http service inside our app
	MaServer	*server;				// For the HTTP server
	Mpr			mpr("simpleEsp");		// Initialize the run time

#if BLD_FEATURE_LOG
	//
	//	Do the following two statements only if you want debug trace
	//
	mpr.addListener(new MprLogToFile());
	mpr.setLogSpec("stdout:4");
#endif

	//
	//	Start the Embedthis Portable Runtime with 10 pool threads
	//
	mpr.start(MPR_SERVICE_THREAD);

#if BLD_FEATURE_MULTITHREAD
	mpr.poolService->setMaxPoolThreads(10);
#endif

	//
	//	Create Http and Server objects for this application. We set the
	//	ServerName to be "default" and the initial serverRoot to be ".".
	//	This will be overridden in simpleEsp.conf.
	//
	http = new MaHttp();
	server = new MaServer(http, "default", ".");
	
	//
	//	Activate the ESP module and handler
	//
	new MaEspModule(0);
	new MaCopyModule(0);

	//
	//	Configure the server with the configuration directive file
	//
	if (server->configure("simpleEsp.conf") < 0) {
		mprFprintf(MPR_STDERR, 
			"Can't configure the server. Error on line %d\n", 
			server->getLine());
		exit(2);
	}

	//
	//	Define our ESP procedures
	//
	espDefineStringCFunction(0, "helloWorld", helloWorld, 0);
	
	//
	//	Start the server
	//
	if (http->start() < 0) {
		mprFprintf(MPR_STDERR, "Can't start the server\n");
		exit(2);
	}

	//
	//	Tell the MPR to loop servicing incoming requests. We can 
	//	replace this call with a variety of event servicing 
	//	mechanisms offered by AppWeb.
	//
	mpr.serviceEvents(0, -1);

	//
	//	Orderly shutdown
	//
	http->stop();
	delete server;
	delete http;

	//
	//	MPR run-time will automatically stop and be cleaned up
	//
	return 0;
}
Пример #18
0
/**
 * For a given query, get a runner.  The runner could be a SingleSolutionRunner, a
 * CachedQueryRunner, or a MultiPlanRunner, depending on the cache/query solver/etc.
 */
Status getRunner(Collection* collection, CanonicalQuery* rawCanonicalQuery,
                 Runner** out, size_t plannerOptions) {

    verify(rawCanonicalQuery);
    auto_ptr<CanonicalQuery> canonicalQuery(rawCanonicalQuery);

    // This can happen as we're called by internal clients as well.
    if (NULL == collection) {
        const string& ns = canonicalQuery->ns();
        *out = new EOFRunner(canonicalQuery.release(), ns);
        return Status::OK();
    }

    // If we have an _id index we can use the idhack runner.
    if (canUseIDHack(*canonicalQuery) && collection->getIndexCatalog()->findIdIndex()) {
        *out = new IDHackRunner(collection, canonicalQuery.release());
        return Status::OK();
    }

    // If it's not NULL, we may have indices.  Access the catalog and fill out IndexEntry(s)
    QueryPlannerParams plannerParams;

    IndexCatalog::IndexIterator ii = collection->getIndexCatalog()->getIndexIterator(false);
    while (ii.more()) {
        const IndexDescriptor* desc = ii.next();
        plannerParams.indices.push_back(IndexEntry(desc->keyPattern(),
                                        desc->isMultikey(),
                                        desc->isSparse(),
                                        desc->indexName(),
                                        desc->infoObj()));
    }

    // If query supports admin hint, filter params.indices by indexes in query settings.
    QuerySettings* querySettings = collection->infoCache()->getQuerySettings();
    AllowedIndices* allowedIndicesRaw;

    // Filter index catalog if admin hint is specified for query.
    // Also, signal to planner that application hint should be ignored.
    if (querySettings->getAllowedIndices(*canonicalQuery, &allowedIndicesRaw)) {
        boost::scoped_ptr<AllowedIndices> allowedIndices(allowedIndicesRaw);
        filterAllowedIndexEntries(*allowedIndices, &plannerParams.indices);
        plannerParams.adminHintApplied = true;
    }

    // Tailable: If the query requests tailable the collection must be capped.
    if (canonicalQuery->getParsed().hasOption(QueryOption_CursorTailable)) {
        if (!collection->isCapped()) {
            return Status(ErrorCodes::BadValue,
                          "error processing query: " + canonicalQuery->toString() +
                          " tailable cursor requested on non capped collection");
        }

        // If a sort is specified it must be equal to expectedSort.
        const BSONObj expectedSort = BSON("$natural" << 1);
        const BSONObj& actualSort = canonicalQuery->getParsed().getSort();
        if (!actualSort.isEmpty() && !(actualSort == expectedSort)) {
            return Status(ErrorCodes::BadValue,
                          "error processing query: " + canonicalQuery->toString() +
                          " invalid sort specified for tailable cursor: "
                          + actualSort.toString());
        }
    }

    // Process the planning options.
    plannerParams.options = plannerOptions;
    if (storageGlobalParams.noTableScan) {
        const string& ns = canonicalQuery->ns();
        // There are certain cases where we ignore this restriction:
        bool ignore = canonicalQuery->getQueryObj().isEmpty()
                      || (string::npos != ns.find(".system."))
                      || (0 == ns.find("local."));
        if (!ignore) {
            plannerParams.options |= QueryPlannerParams::NO_TABLE_SCAN;
        }
    }

    if (!(plannerParams.options & QueryPlannerParams::NO_TABLE_SCAN)) {
        plannerParams.options |= QueryPlannerParams::INCLUDE_COLLSCAN;
    }

    // If the caller wants a shard filter, make sure we're actually sharded.
    if (plannerParams.options & QueryPlannerParams::INCLUDE_SHARD_FILTER) {
        CollectionMetadataPtr collMetadata =
            shardingState.getCollectionMetadata(canonicalQuery->ns());

        if (collMetadata) {
            plannerParams.shardKey = collMetadata->getKeyPattern();
        }
        else {
            // If there's no metadata don't bother w/the shard filter since we won't know what
            // the key pattern is anyway...
            plannerParams.options &= ~QueryPlannerParams::INCLUDE_SHARD_FILTER;
        }
    }

    // Try to look up a cached solution for the query.
    //
    // Skip cache look up for non-cacheable queries.
    // See PlanCache::shouldCacheQuery()
    //
    // TODO: Can the cache have negative data about a solution?
    CachedSolution* rawCS;
    if (PlanCache::shouldCacheQuery(*canonicalQuery) &&
            collection->infoCache()->getPlanCache()->get(*canonicalQuery, &rawCS).isOK()) {
        // We have a CachedSolution.  Have the planner turn it into a QuerySolution.
        boost::scoped_ptr<CachedSolution> cs(rawCS);
        QuerySolution *qs, *backupQs;
        Status status = QueryPlanner::planFromCache(*canonicalQuery, plannerParams, *cs,
                        &qs, &backupQs);
        if (status.isOK()) {
            WorkingSet* ws;
            PlanStage* root;
            verify(StageBuilder::build(*qs, &root, &ws));
            CachedPlanRunner* cpr = new CachedPlanRunner(canonicalQuery.release(), qs,
                    root, ws);

            if (NULL != backupQs) {
                WorkingSet* backupWs;
                PlanStage* backupRoot;
                verify(StageBuilder::build(*backupQs, &backupRoot, &backupWs));
                cpr->setBackupPlan(backupQs, backupRoot, backupWs);
            }

            *out = cpr;
            return Status::OK();
        }
    }

    plannerParams.options |= QueryPlannerParams::INDEX_INTERSECTION;
    plannerParams.options |= QueryPlannerParams::KEEP_MUTATIONS;

    vector<QuerySolution*> solutions;
    Status status = QueryPlanner::plan(*canonicalQuery, plannerParams, &solutions);
    if (!status.isOK()) {
        return Status(ErrorCodes::BadValue,
                      "error processing query: " + canonicalQuery->toString() +
                      " planner returned error: " + status.reason());
    }

    /*
    for (size_t i = 0; i < solutions.size(); ++i) {
        QLOG() << "solution " << i << " is " << solutions[i]->toString() << endl;
    }
    */

    // We cannot figure out how to answer the query.  Should this ever happen?
    if (0 == solutions.size()) {
        return Status(ErrorCodes::BadValue,
                      "error processing query: " + canonicalQuery->toString() +
                      " No query solutions");
    }

    if (1 == solutions.size()) {
        // Only one possible plan.  Run it.  Build the stages from the solution.
        WorkingSet* ws;
        PlanStage* root;
        verify(StageBuilder::build(*solutions[0], &root, &ws));

        // And, run the plan.
        *out = new SingleSolutionRunner(canonicalQuery.release(), solutions[0], root, ws);
        return Status::OK();
    }
    else {
        // Many solutions.  Let the MultiPlanRunner pick the best, update the cache, and so on.
        auto_ptr<MultiPlanRunner> mpr(new MultiPlanRunner(canonicalQuery.release()));
        for (size_t i = 0; i < solutions.size(); ++i) {
            WorkingSet* ws;
            PlanStage* root;
            if (solutions[i]->cacheData.get()) {
                solutions[i]->cacheData->adminHintApplied = plannerParams.adminHintApplied;
            }
            verify(StageBuilder::build(*solutions[i], &root, &ws));
            // Takes ownership of all arguments.
            mpr->addPlan(solutions[i], root, ws);
        }
        *out = mpr.release();
        return Status::OK();
    }
}
Пример #19
0
int
pln_damage(struct plnstr *pp, char type, int noisy)
{
    struct plchrstr *pcp = plchr + pp->pln_type;
    int load, i, hitroll, aim, len;
    int dam = 0;
    int effective = 1;
    int pinbomber = 0;
    char buf[80];

    if (CANT_HAPPEN(nuk_on_plane(pp) >= 0))
	return 0;

    load = pln_load(pp);
    if (!load)		       /* e.g. ab, blowing up on launch pad */
	return 0;

    i = roll(load) + 1;
    if (i > load)
	i = load;

    if (pcp->pl_flags & P_M) {
	if (pcp->pl_flags & P_MAR)
	    pinbomber = 1;
    } else if (pcp->pl_flags & P_T)
	pinbomber = 1;

    aim = pln_acc(pp);
    if (type == 's') {
	effective = !pinbomber;
	aim = 30 + (pinbomber ? aim : 100 - aim);
    } else {
	effective = pinbomber;
	aim = 100 - aim;
    }

    len = 0;
    while (i--) {
	dam += roll(6);
	hitroll = roll(100);
	if (hitroll >= 90) {
	    dam += 8;
	    if (noisy)
		len += sprintf(buf + len, "BLAM");
	} else if (hitroll < aim) {
	    dam += 5;
	    if (noisy)
		len += sprintf(buf + len, "Blam");
	} else {
	    dam += 1;
	    if (noisy)
		len += sprintf(buf + len, "blam");
	}
	if (noisy) {
	    if (len > 75) {
		mpr(pp->pln_own, "%s\n", buf);
		len = 0;
	    }
	    if (i)
		len += sprintf(buf + len, "-");
	}
    }
    if (noisy && len)
	mpr(pp->pln_own, "%s\n", buf);
    if (effective)
	dam *= 2;
    return dam;
}
Пример #20
0
void
takeover(struct sctstr *sp, natid newown)
{
    struct plnstr *pp;
    struct lndstr *lp;
    int civ;
    int che_count;
    int oldche;
    int n;
    struct nstr_item ni;
    struct plnstr p;
    struct lndstr land;

    /* Wipe all the distribution info */
    memset(sp->sct_dist, 0, sizeof(sp->sct_dist));
    memset(sp->sct_del, 0, sizeof(sp->sct_del));
    if (sp->sct_own == 0)
	sp->sct_off = 0;
    else
	sp->sct_off = 1;
    sp->sct_dist_x = sp->sct_x;
    sp->sct_dist_y = sp->sct_y;

    pp = &p;
    /* Take over planes */
    snxtitem_xy(&ni, EF_PLANE, sp->sct_x, sp->sct_y);
    while (nxtitem(&ni, pp)) {
	if (pp->pln_own != sp->sct_own)
	    continue;
	takeover_plane(pp, newown);
    }

    /* Take over land units */
    lp = &land;
    snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
    while (nxtitem(&ni, lp)) {
	if ((lp->lnd_own == newown) || (lp->lnd_own == 0))
	    continue;
	if (lp->lnd_own != sp->sct_own)
	    continue;
	if (lp->lnd_ship >= 0 || lp->lnd_land >= 0)
	    continue;
	/* Spies get a chance to hide */
	if (lchr[(int)lp->lnd_type].l_flags & L_SPY) {
	    if (!(chance(LND_SPY_DETECT_CHANCE(lp->lnd_effic))))
		continue;
	}
	n = lp->lnd_effic - (29 + roll(100));
	if (n < 0)
	    n = 0;
	lp->lnd_effic = n;
	if (lp->lnd_effic < LAND_MINEFF) {
	    lp->lnd_effic = 0;
	    mpr(newown, "%s blown up by the crew!\n", prland(lp));
	    wu(0, lp->lnd_own,
	       "%s blown up by the crew when %s took %s!\n",
	       prland(lp),
	       cname(newown), xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
	} else {
	    mpr(newown, "We have captured %s!\n", prland(lp));
	    wu(0, lp->lnd_own,
	       "%s captured when %s took %s!\n",
	       prland(lp),
	       cname(newown), xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
	}
	takeover_land(lp, newown);
    }

    sp->sct_avail = 0;
    civ = sp->sct_item[I_CIVIL];
    oldche = sp->sct_che;
    /*
     * create guerrillas from civilians
     * how spunky are these guys?
     * n: random number from -25:75 + (50 - loyalty)
     */
    n = (50 - sp->sct_loyal) + (roll(100) - 26);
    if (n > 0 && sp->sct_own == sp->sct_oldown) {
	che_count = (civ * n / 3000) + 5;
	if (che_count * 2 > civ)
	    che_count = civ / 2;
	che_count /= hap_fact(getnatp(newown), getnatp(sp->sct_own));
	if (che_count + oldche > CHE_MAX)
	    che_count = CHE_MAX - oldche;
	if (che_count > 0) {
	    civ -= che_count;
	    che_count += oldche;
	} else
	    che_count = oldche;
    } else
	che_count = oldche;
    sp->sct_che = che_count;
    if (newown != sp->sct_oldown)
	sp->sct_che_target = newown;
    if (sp->sct_che_target == 0)
	sp->sct_che = 0;
    sp->sct_item[I_CIVIL] = civ;
    if (sp->sct_oldown == newown || civ == 0) {
	/*
	 * taking over one of your old sectors
	 */
	sp->sct_loyal = 0;
	sp->sct_oldown = newown;
    } else {
	/*
	 * taking over someone else's sector
	 */
	sp->sct_loyal = 50;
    }
    sp->sct_own = newown;
    if (opt_MOB_ACCESS) {
	game_tick_to_now(&sp->sct_access);
	sp->sct_mobil = -(etu_per_update / sect_mob_neg_factor);
    } else {
	sp->sct_mobil = 0;
    }
}
Пример #21
0
void
setrel(natid us, natid them, int rel)
{
    struct natstr *mynp = getnatp(us);
    struct natstr *themnp = getnatp(them);
    int oldrel;
    char *whichway;
    int n_up = 0;
    int n_down = 0;
    char *addendum = NULL;

    if (CANT_HAPPEN(rel < AT_WAR))
	rel = AT_WAR;
    if (CANT_HAPPEN(rel > ALLIED))
	rel = ALLIED;
    if (CANT_HAPPEN(!mynp || !themnp))
	return;
    if (us == them)
	return;
    oldrel = relations_with(us, them);
    if (oldrel == rel)
	return;
    if (rel > oldrel)
	whichway = "upgraded";
    else
	whichway = "downgraded";
    if (rel == ALLIED) {
	addendum = "Congratulations!";
	n_up = N_DECL_ALLY;
    } else if (rel == FRIENDLY) {
	n_up = N_UP_FRIENDLY;
	n_down = N_DOWN_FRIENDLY;
    } else if (rel == NEUTRAL) {
	n_up = N_UP_NEUTRAL;
	n_down = N_DOWN_NEUTRAL;
    } else if (rel == HOSTILE) {
	addendum = "Another cold war...";
	n_up = N_UP_HOSTILE;
	n_down = N_DOWN_HOSTILE;
    } else if (rel < HOSTILE) {
	addendum = "Declaration made (give 'em hell).";
	n_down = N_DECL_WAR;
    }

    if (addendum && us == player->cnum && !update_running)
	pr("%s\n", addendum);
    mpr(us, "Diplomatic relations with %s %s to \"%s\".\n",
	cname(them), whichway, relates[rel]);
    if (!(getrejects(us, themnp) & REJ_TELE))
	mpr(them,
	    "Country %s has %s their relations with you to \"%s\"!\n",
	    prnat(mynp), whichway, relates[rel]);

    putrel(mynp, them, rel);
    putnat(mynp);

    if (!player->god) {
	if (oldrel == ALLIED)
	    nreport(us, N_DIS_ALLY, them, 1);
	else if (oldrel < HOSTILE && rel >= HOSTILE)
	    nreport(us, N_DIS_WAR, them, 1);
	if (rel > oldrel)
	    nreport(us, n_up, them, 1);
	else
	    nreport(us, n_down, them, 1);
    }
    if (opt_HIDDEN)
	setcont(them, us, FOUND_TELE);
}
Пример #22
0
    /**
     * For a given query, get a runner.  The runner could be a SingleSolutionRunner, a
     * CachedQueryRunner, or a MultiPlanRunner, depending on the cache/query solver/etc.
     */
    Status getRunner(CanonicalQuery* rawCanonicalQuery, Runner** out) {
        verify(rawCanonicalQuery);
        auto_ptr<CanonicalQuery> canonicalQuery(rawCanonicalQuery);

        // Try to look up a cached solution for the query.
        // TODO: Can the cache have negative data about a solution?
        PlanCache* localCache = PlanCache::get(canonicalQuery->ns());
        if (NULL != localCache) {
            CachedSolution* cs = localCache->get(*canonicalQuery);
            if (NULL != cs) {
                // We have a cached solution.  Hand the canonical query and cached solution off to
                // the cached plan runner, which takes ownership of both.
                WorkingSet* ws;
                PlanStage* root;
                verify(StageBuilder::build(*cs->solution, &root, &ws));
                *out = new CachedPlanRunner(canonicalQuery.release(), cs, root, ws);
                return Status::OK();
            }
        }

        // No entry in cache for the query.  We have to solve the query ourself.

        // Get the indices that we could possibly use.
        NamespaceDetails* nsd = nsdetails(canonicalQuery->ns().c_str());

        // If this is NULL, there is no data but the query is valid.  You're allowed to query for
        // data on an empty collection and it's not an error.  There just isn't any data...
        if (NULL == nsd) {
            const std::string& ns = canonicalQuery->ns();
            *out = new EOFRunner(canonicalQuery.release(), ns);
            return Status::OK();
        }

        // Tailable: If the query requests tailable the collection must be capped.
        if (canonicalQuery->getParsed().hasOption(QueryOption_CursorTailable)) {
            if (!nsd->isCapped()) {
                return Status(ErrorCodes::BadValue,
                              "tailable cursor requested on non capped collection");
            }

            // If a sort is specified it must be equal to expectedSort.
            const BSONObj expectedSort = BSON("$natural" << 1);
            const BSONObj& actualSort = canonicalQuery->getParsed().getSort();
            if (!actualSort.isEmpty() && !(actualSort == expectedSort)) {
                return Status(ErrorCodes::BadValue,
                              "invalid sort specified for tailable cursor: "
                              + actualSort.toString());
            }
        }

        // If it's not NULL, we may have indices.
        vector<IndexEntry> indices;
        for (int i = 0; i < nsd->getCompletedIndexCount(); ++i) {
            auto_ptr<IndexDescriptor> desc(CatalogHack::getDescriptor(nsd, i));
            indices.push_back(IndexEntry(desc->keyPattern(), desc->isMultikey(), desc->isSparse(), desc->indexName()));
        }

        vector<QuerySolution*> solutions;
        size_t options = QueryPlanner::DEFAULT;
        if (storageGlobalParams.noTableScan) {
            const string& ns = canonicalQuery->ns();
            // There are certain cases where we ignore this restriction:
            bool ignore = canonicalQuery->getQueryObj().isEmpty()
                          || (string::npos != ns.find(".system."))
                          || (0 == ns.find("local."));
            if (!ignore) {
                options |= QueryPlanner::NO_TABLE_SCAN;
            }
        }
        else {
            options |= QueryPlanner::INCLUDE_COLLSCAN;
        }
        QueryPlanner::plan(*canonicalQuery, indices, options, &solutions);

        /*
        for (size_t i = 0; i < solutions.size(); ++i) {
            QLOG() << "solution " << i << " is " << solutions[i]->toString() << endl;
        }
        */

        // We cannot figure out how to answer the query.  Should this ever happen?
        if (0 == solutions.size()) {
            return Status(ErrorCodes::BadValue, "Can't create a plan for the canonical query " +
                                                 canonicalQuery->toString());
        }

        if (1 == solutions.size()) {
            // Only one possible plan.  Run it.  Build the stages from the solution.
            WorkingSet* ws;
            PlanStage* root;
            verify(StageBuilder::build(*solutions[0], &root, &ws));

            // And, run the plan.
            *out = new SingleSolutionRunner(canonicalQuery.release(), solutions[0], root, ws);
            return Status::OK();
        }
        else {
            // Many solutions.  Let the MultiPlanRunner pick the best, update the cache, and so on.
            auto_ptr<MultiPlanRunner> mpr(new MultiPlanRunner(canonicalQuery.release()));
            for (size_t i = 0; i < solutions.size(); ++i) {
                WorkingSet* ws;
                PlanStage* root;
                verify(StageBuilder::build(*solutions[i], &root, &ws));
                // Takes ownership of all arguments.
                mpr->addPlan(solutions[i], root, ws);
            }
            *out = mpr.release();
            return Status::OK();
        }
    }
Пример #23
0
    /**
     * For a given query, get a runner.  The runner could be a SingleSolutionRunner, a
     * CachedQueryRunner, or a MultiPlanRunner, depending on the cache/query solver/etc.
     */
    Status getRunner(CanonicalQuery* rawCanonicalQuery, Runner** out, size_t plannerOptions) {
        verify(rawCanonicalQuery);
        auto_ptr<CanonicalQuery> canonicalQuery(rawCanonicalQuery);

        // Try to look up a cached solution for the query.
        // TODO: Can the cache have negative data about a solution?
        PlanCache* localCache = PlanCache::get(canonicalQuery->ns());
        if (NULL != localCache) {
            CachedSolution* cs = localCache->get(*canonicalQuery);
            if (NULL != cs) {
                // We have a cached solution.  Hand the canonical query and cached solution off to
                // the cached plan runner, which takes ownership of both.
                WorkingSet* ws;
                PlanStage* root;
                verify(StageBuilder::build(*cs->solution, &root, &ws));
                *out = new CachedPlanRunner(canonicalQuery.release(), cs, root, ws);
                return Status::OK();
            }
        }

        // No entry in cache for the query.  We have to solve the query ourself.

        // Get the indices that we could possibly use.
        Database* db = cc().database();
        verify( db );
        Collection* collection = db->getCollection( canonicalQuery->ns() );

        // This can happen as we're called by internal clients as well.
        if (NULL == collection) {
            const string& ns = canonicalQuery->ns();
            *out = new EOFRunner(canonicalQuery.release(), ns);
            return Status::OK();
        }

        // If we have an _id index we can use the idhack runner.
        if (canUseIDHack(*canonicalQuery) && collection->getIndexCatalog()->findIdIndex()) {
            *out = new IDHackRunner(collection, canonicalQuery.release());
            return Status::OK();
        }

        // If it's not NULL, we may have indices.  Access the catalog and fill out IndexEntry(s)
        QueryPlannerParams plannerParams;
        for (int i = 0; i < collection->getIndexCatalog()->numIndexesReady(); ++i) {
            IndexDescriptor* desc = collection->getIndexCatalog()->getDescriptor( i );
            plannerParams.indices.push_back(IndexEntry(desc->keyPattern(),
                                                       desc->isMultikey(),
                                                       desc->isSparse(),
                                                       desc->indexName()));
        }

        // Tailable: If the query requests tailable the collection must be capped.
        if (canonicalQuery->getParsed().hasOption(QueryOption_CursorTailable)) {
            if (!collection->isCapped()) {
                return Status(ErrorCodes::BadValue,
                              "tailable cursor requested on non capped collection");
            }

            // If a sort is specified it must be equal to expectedSort.
            const BSONObj expectedSort = BSON("$natural" << 1);
            const BSONObj& actualSort = canonicalQuery->getParsed().getSort();
            if (!actualSort.isEmpty() && !(actualSort == expectedSort)) {
                return Status(ErrorCodes::BadValue,
                              "invalid sort specified for tailable cursor: "
                              + actualSort.toString());
            }
        }

        // Process the planning options.
        plannerParams.options = plannerOptions;
        if (storageGlobalParams.noTableScan) {
            const string& ns = canonicalQuery->ns();
            // There are certain cases where we ignore this restriction:
            bool ignore = canonicalQuery->getQueryObj().isEmpty()
                          || (string::npos != ns.find(".system."))
                          || (0 == ns.find("local."));
            if (!ignore) {
                plannerParams.options |= QueryPlannerParams::NO_TABLE_SCAN;
            }
        }

        if (!(plannerParams.options & QueryPlannerParams::NO_TABLE_SCAN)) {
            plannerParams.options |= QueryPlannerParams::INCLUDE_COLLSCAN;
        }

        // If the caller wants a shard filter, make sure we're actually sharded.
        if (plannerParams.options & QueryPlannerParams::INCLUDE_SHARD_FILTER) {
            CollectionMetadataPtr collMetadata = shardingState.getCollectionMetadata(canonicalQuery->ns());
            if (collMetadata) {
                plannerParams.shardKey = collMetadata->getKeyPattern();
            }
            else {
                // If there's no metadata don't bother w/the shard filter since we won't know what
                // the key pattern is anyway...
                plannerParams.options &= ~QueryPlannerParams::INCLUDE_SHARD_FILTER;
            }
        }

        vector<QuerySolution*> solutions;
        QueryPlanner::plan(*canonicalQuery, plannerParams, &solutions);

        /*
        for (size_t i = 0; i < solutions.size(); ++i) {
            QLOG() << "solution " << i << " is " << solutions[i]->toString() << endl;
        }
        */

        // We cannot figure out how to answer the query.  Should this ever happen?
        if (0 == solutions.size()) {
            return Status(ErrorCodes::BadValue, "No query solutions");
        }

        if (1 == solutions.size()) {
            // Only one possible plan.  Run it.  Build the stages from the solution.
            WorkingSet* ws;
            PlanStage* root;
            verify(StageBuilder::build(*solutions[0], &root, &ws));

            // And, run the plan.
            *out = new SingleSolutionRunner(canonicalQuery.release(), solutions[0], root, ws);
            return Status::OK();
        }
        else {
            // Many solutions.  Let the MultiPlanRunner pick the best, update the cache, and so on.
            auto_ptr<MultiPlanRunner> mpr(new MultiPlanRunner(canonicalQuery.release()));
            for (size_t i = 0; i < solutions.size(); ++i) {
                WorkingSet* ws;
                PlanStage* root;
                verify(StageBuilder::build(*solutions[i], &root, &ws));
                // Takes ownership of all arguments.
                mpr->addPlan(solutions[i], root, ws);
            }
            *out = mpr.release();
            return Status::OK();
        }
    }
Пример #24
0
    Status getRunnerAlwaysPlan(Collection* collection,
                               CanonicalQuery* rawCanonicalQuery,
                               const QueryPlannerParams& plannerParams,
                               Runner** out) {

        invariant(collection);
        invariant(rawCanonicalQuery);
        auto_ptr<CanonicalQuery> canonicalQuery(rawCanonicalQuery);

        vector<QuerySolution*> solutions;
        Status status = QueryPlanner::plan(*canonicalQuery, plannerParams, &solutions);
        if (!status.isOK()) {
            return Status(ErrorCodes::BadValue,
                          "error processing query: " + canonicalQuery->toString() +
                          " planner returned error: " + status.reason());
        }

        // We cannot figure out how to answer the query.  Perhaps it requires an index
        // we do not have?
        if (0 == solutions.size()) {
            return Status(ErrorCodes::BadValue,
                          str::stream()
                          << "error processing query: "
                          << canonicalQuery->toString()
                          << " No query solutions");
        }

        // See if one of our solutions is a fast count hack in disguise.
        if (plannerParams.options & QueryPlannerParams::PRIVATE_IS_COUNT) {
            for (size_t i = 0; i < solutions.size(); ++i) {
                if (turnIxscanIntoCount(solutions[i])) {
                    // Great, we can use solutions[i].  Clean up the other QuerySolution(s).
                    for (size_t j = 0; j < solutions.size(); ++j) {
                        if (j != i) {
                            delete solutions[j];
                        }
                    }

                    LOG(2) << "Using fast count: " << canonicalQuery->toStringShort()
                           << ", planSummary: " << getPlanSummary(*solutions[i]);

                    // We're not going to cache anything that's fast count.
                    WorkingSet* ws;
                    PlanStage* root;
                    verify(StageBuilder::build(collection, *solutions[i], &root, &ws));
                    *out = new SingleSolutionRunner(collection,
                                                    canonicalQuery.release(),
                                                    solutions[i],
                                                    root,
                                                    ws);
                    return Status::OK();
                }
            }
        }

        if (1 == solutions.size()) {
            LOG(2) << "Only one plan is available; it will be run but will not be cached. "
                   << canonicalQuery->toStringShort()
                   << ", planSummary: " << getPlanSummary(*solutions[0]);

            // Only one possible plan.  Run it.  Build the stages from the solution.
            WorkingSet* ws;
            PlanStage* root;
            verify(StageBuilder::build(collection, *solutions[0], &root, &ws));

            // And, run the plan.
            *out = new SingleSolutionRunner(collection,
                                            canonicalQuery.release(),
                                            solutions[0],
                                            root,
                                            ws);
            return Status::OK();
        }
        else {
            // Many solutions.  Let the MultiPlanRunner pick the best, update the cache, and so on.
            auto_ptr<MultiPlanRunner> mpr(new MultiPlanRunner(collection,canonicalQuery.release()));

            for (size_t i = 0; i < solutions.size(); ++i) {
                WorkingSet* ws;
                PlanStage* root;
                if (solutions[i]->cacheData.get()) {
                    solutions[i]->cacheData->indexFilterApplied = plannerParams.indexFiltersApplied;
                }
                verify(StageBuilder::build(collection, *solutions[i], &root, &ws));
                // Takes ownership of all arguments.
                mpr->addPlan(solutions[i], root, ws);
            }
            *out = mpr.release();
            return Status::OK();
        }
    }
Пример #25
0
static int
msl_intercept(struct plnstr *msl, struct sctstr *sp, int sublaunch,
	      struct emp_qelem *irvlist, char *att_name, char *def_name,
	      int news_item)
{
    struct plnstr *pp;
    struct emp_qelem *intlist;
    struct emp_qelem intfoo;
    struct emp_qelem *qp;
    struct emp_qelem *next;
    struct plist *ip;
    int icount = 0;
    short destroyed;
    char *who = sublaunch ? "" : cname(msl->pln_own);

    intlist = &intfoo;
    emp_initque(intlist);
    /* First choose interceptors belonging to the target sector */
    /* only allow two defense missiles per missile attack */
    for (qp = irvlist->q_forw; qp != irvlist && icount < 2; qp = next) {
	next = qp->q_forw;
	ip = (struct plist *)qp;
	pp = &ip->plane;
	if (pp->pln_own != sp->sct_own)
	    continue;
	if (mission_pln_equip(ip, NULL, 'i') < 0) {
	    emp_remque(qp);
	    free(qp);
	    continue;
	}
	/* got one interceptor, delete from irv_list and
	 * add to  int_list.
	 */
	emp_remque(qp);
	emp_insque(qp, intlist);
	putplane(pp->pln_uid, pp);
	icount++;
    }
    /* only allow two defense missiles per missile attack */
    for (qp = irvlist->q_forw; qp != irvlist && icount < 2; qp = next) {
	next = qp->q_forw;
	ip = (struct plist *)qp;
	pp = &ip->plane;
	if (mission_pln_equip(ip, NULL, 'i') < 0) {
	    emp_remque(qp);
	    free(qp);
	    continue;
	}
	/* got one interceptor, delete from irv_list and
	 * add to  int_list.
	 */
	emp_remque(qp);
	emp_insque(qp, intlist);
	putplane(pp->pln_uid, pp);
	icount++;
    }
    /* Now, clean out the queue */
    while (!QEMPTY(irvlist)) {
	qp = irvlist->q_forw;
	emp_remque(qp);
	free(qp);
    }
    if (icount == 0) {
	mpr(sp->sct_own, "No %ss launched to intercept.\n", def_name);
	return 0;
    }

    /* attempt to destroy incoming missile */

    destroyed = 0;
    while (!destroyed && !QEMPTY(intlist)) {
	qp = intlist->q_forw;
	ip = (struct plist *)qp;
	pp = &ip->plane;

	mpr(msl->pln_own, "%s %s launched in defense!\n",
	    cname(pp->pln_own), def_name);
	if (sp->sct_own == pp->pln_own) {
	    mpr(sp->sct_own, "%s launched to intercept %s %s!\n",
		def_name, who, att_name);
	} else {
	    mpr(sp->sct_own,
		"%s launched an %s to intercept the %s %s!\n",
		cname(pp->pln_own), def_name, who, att_name);
	    mpr(pp->pln_own,
		"%s launched to intercept %s %s arcing towards %s territory!\n",
		def_name, who, att_name, cname(sp->sct_own));
	}

	if (msl_launch(pp, EF_PLANE, att_name, sp->sct_x, sp->sct_y,
		       msl->pln_own, NULL) >= 0
	    && msl_hit(pp, pln_def(msl), EF_PLANE, 0, 0, 0, msl->pln_own)) {
	    mpr(msl->pln_own, "%s destroyed by %s %s!\n",
		att_name, cname(pp->pln_own), def_name);
	    mpr(sp->sct_own, "%s %s intercepted!\n", who, att_name);
	    if (sp->sct_own != pp->pln_own)
		mpr(pp->pln_own, "%s %s intercepted!\n", who, att_name);
	    if (sublaunch)
		nreport(pp->pln_own, news_item, 0, 1);
	    else
		nreport(pp->pln_own, news_item, msl->pln_own, 1);
	    destroyed = 1;
	}
	/* zap the missile */
	pp->pln_effic = 0;
	putplane(pp->pln_uid, pp);
	emp_remque(qp);
	free(qp);
    }
    /* Clean out what is left in the list */
    while (!QEMPTY(intlist)) {
	qp = intlist->q_forw;
	emp_remque(qp);
	free(qp);
    }
    if (destroyed)
	return 1;
    if (icount) {
	mpr(msl->pln_own, "%s made it through %s defenses!\n",
	    att_name, def_name);
	mpr(sp->sct_own, "%s made it through %s defenses!\n",
	    att_name, def_name);
    }
    return 0;
}
Пример #26
0
int
msl_launch(struct plnstr *pp, int type, char *what, coord x, coord y,
	   natid victim, int *sublaunchp)
{
    struct shpstr ship;
    struct nukstr nuke;
    int sublaunch = 0;
    char *base, *in_or_at, *from;

    mpr(pp->pln_own, "Preparing to launch %s at %s %s %s%s\n",
	prplane(pp),
	cname(victim),
	what,
	type != EF_SECTOR ? "in " : "",
	xyas(x, y, pp->pln_own));
    if (pp->pln_ship >= 0) {
	getship(pp->pln_ship, &ship);
	base = prship(&ship);
	in_or_at = " in ";
	if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
	    sublaunch = 1;
	    from = "in hatch";
	} else
	    from = "on deck";
    } else {
	if (pp->pln_harden > 0) {
	    base = "missile silo";
	    in_or_at = " at ";
	    from = "in silo";
	} else {
	    base = in_or_at = "";
	    from = "on launch pad";
	}
    }
    mpr(pp->pln_own, "\tLaunching from %s%s%s\n",
	base, in_or_at, xyas(pp->pln_x, pp->pln_y, pp->pln_own));

    CANT_HAPPEN(pp->pln_flags & PLN_LAUNCHED);
    pp->pln_flags |= PLN_LAUNCHED;
    putplane(pp->pln_uid, pp);

    if (chance((0.05 + (100 - pp->pln_effic) / 100.0)
	       * (1 - techfact(pp->pln_tech, 1.0)))) {
	mpr(pp->pln_own, "KABOOOOM!  Missile explodes %s!\n", from);
	if (getnuke(nuk_on_plane(pp), &nuke)) {
	    mpr(pp->pln_own, "%s lost!\n", prnuke(&nuke));
	    nuke.nuk_effic = 0;
	    putnuke(nuke.nuk_uid, &nuke);
	}
#if 0
	/*
	 * Disabled for now, because it breaks callers that call
	 * msl_launch() for each member of a list of planes, created
	 * by msl_sel() or perform_mission().  Damage to the base can
	 * damage other planes.  Any copies of them in the list become
	 * stale.  When msl_launch() modifies and writes back such a
	 * stale copy, the damage gets wiped out, triggering a seqno
	 * oops.
	 */
	if (chance(0.33)) {
	    struct sctstr sect;
	    int dam;

	    dam = pln_damage(pp, 'p', NULL) / 2;
	    if (pp->pln_ship >= 0) {
		shipdamage(&ship, dam);
		putship(ship.shp_uid, &ship);
	    } else {
		mpr(pp->pln_own, "Explosion damages %s %d%%\n",
		    xyas(pp->pln_x, pp->pln_y, pp->pln_own), dam);
		getsect(pp->pln_x, pp->pln_y, &sect);
		sectdamage(&sect, dam);
		putsect(&sect);
	    }
	}
#endif
	return -1;
    }

    mpr(pp->pln_own, "\tSHWOOOOOSH!  Missile launched!\n");

    if (type != EF_PLANE)
	mpr(victim, "Incoming %s missile sighted at %s...\n",
	    sublaunch ? "sub-launched" : cname(pp->pln_own),
	    xyas(x, y, victim));

    if (type == EF_SECTOR || type == EF_LAND) {
	if (msl_abm_intercept(pp, x, y, sublaunch))
	    return -1;
    }
    if (type == EF_SHIP) {
	if (shp_missile_defense(x, y, pp->pln_own, pln_def(pp))) {
	    return -1;
	}
    }

    if (sublaunchp)
	*sublaunchp = sublaunch;
    return 0;
}
Пример #27
0
/* Knock down a bridge span.  Note that this does NOT write the
 * sector out to the database, it's up to the caller to do that. */
static void
knockdown(struct sctstr *sp)
{
    struct lndstr land;
    struct plnstr plane;
    struct nukstr nuke;
    struct nstr_item ni;

    mpr(sp->sct_own,
	"Crumble... SCREEEECH!  Splash! Bridge%s falls at %s!\n",
	sp->sct_type == SCT_BTOWER ? " tower" : "",
	xyas(sp->sct_x, sp->sct_y, sp->sct_own));
    if (!SCT_MINES_ARE_SEAMINES(sp))
	sp->sct_mines = 0;
    sp->sct_type = SCT_WATER;
    sp->sct_newtype = SCT_WATER;
    sp->sct_own = 0;
    sp->sct_oldown = 0;
    sp->sct_mobil = 0;
    sp->sct_effic = 0;

    /* Sink all the units */
    snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
    while (nxtitem(&ni, &land)) {
	if (land.lnd_own == 0)
	    continue;
	if (land.lnd_ship >= 0)
	    continue;
	mpr(land.lnd_own, "     AARGH! %s tumbles to its doom!\n",
	    prland(&land));
	land.lnd_effic = 0;
	putland(land.lnd_uid, &land);
    }
    /* Sink all the planes */
    snxtitem_xy(&ni, EF_PLANE, sp->sct_x, sp->sct_y);
    while (nxtitem(&ni, &plane)) {
	if (plane.pln_own == 0)
	    continue;
	if (plane.pln_flags & PLN_LAUNCHED)
	    continue;
	if (plane.pln_ship >= 0)
	    continue;
	mpr(plane.pln_own, "     AARGH! %s tumbles to its doom!\n",
	    prplane(&plane));
	plane.pln_effic = 0;
	putplane(plane.pln_uid, &plane);
    }
    /* Sink all the nukes */
    snxtitem_xy(&ni, EF_NUKE, sp->sct_x, sp->sct_y);
    while (nxtitem(&ni, &nuke)) {
	if (nuke.nuk_own == 0)
	    continue;
	if (nuke.nuk_plane >= 0)
	    continue;
	mpr(nuke.nuk_own, "     %s sinks to the bottom of the sea!\n",
	    prnuke(&nuke));
	nuke.nuk_effic = 0;
	putnuke(nuke.nuk_uid, &nuke);
    }
    memset(sp->sct_item, 0, sizeof(sp->sct_item));
    memset(sp->sct_del, 0, sizeof(sp->sct_del));
    memset(sp->sct_dist, 0, sizeof(sp->sct_dist));
    sp->sct_pstage = PLG_HEALTHY;
    sp->sct_ptime = 0;
    sp->sct_che = 0;
    sp->sct_che_target = 0;
}
Пример #28
0
int
detonate(struct nukstr *np, coord x, coord y, int airburst)
{
    int nuketype = np->nuk_type;
    struct nchrstr *ncp;
    struct plnstr plane;
    struct sctstr sect;
    struct shpstr ship;
    struct lndstr land;
    struct nukstr nuke;
    natid own;
    int type;
    int damage;
    int fallout;
    int rad;
    struct nstr_sect ns;
    struct nstr_item ni;
    int changed = 0;

    pr("Releasing RV's for %s detonation...\n",
       airburst ? "airburst" : "groundburst");

    getsect(x, y, &sect);
    ncp = &nchr[nuketype];
    kaboom(x, y, ncp->n_blast);
    rad = ncp->n_blast;
    if (!airburst)
        rad = rad * 2 / 3;
    if (sect.sct_type == SCT_WATER)
        rad = 0;     /* Nukes falling on water affect only 1 sector */
    np->nuk_effic = 0;
    putnuke(np->nuk_uid, np);

    snxtsct_dist(&ns, x, y, rad);
    while (nxtsct(&ns, &sect)) {
        own = sect.sct_own;
        type = sect.sct_type;
        if ((damage = nukedamage(ncp, ns.curdist, airburst)) <= 0)
            continue;
        if (type == SCT_SANCT) {
            pr("bounced off %s\n", xyas(ns.x, ns.y, player->cnum));
            mpr(own, "%s nuclear device bounced off %s\n",
                cname(player->cnum), xyas(ns.x, ns.y, own));
            nreport(player->cnum, N_NUKE, own, 1);
            continue;
        }
        sect_damage(&sect, damage);
        if (opt_FALLOUT) {
            fallout = sect.sct_fallout;
            if (ncp->n_flags & N_NEUT)
                fallout += damage * 30;
            else
                fallout += damage * 3;
            sect.sct_fallout = MIN(fallout, FALLOUT_MAX);
        }
        if (damage > 100) {
            sect.sct_oldown = 0;
            sect.sct_own = 0;
            if (type == SCT_WATER || type == SCT_BSPAN ||
                    type == SCT_BTOWER) {
                if (type != SCT_WATER) {
                    pr("left nothing but water in %s\n",
                       xyas(ns.x, ns.y, player->cnum));
                    if (own != player->cnum)
                        mpr(own,
                            "%s nuclear device left nothing but water in %s\n",
                            cname(player->cnum), xyas(ns.x, ns.y, own));
                    sect.sct_newtype = SCT_WATER;
                    sect.sct_type = SCT_WATER;
                }
            } else {
                sect.sct_newtype = SCT_WASTE;
                sect.sct_type = SCT_WASTE;
                pr("turned %s into a radioactive wasteland\n",
                   xyas(ns.x, ns.y, player->cnum));
                if (own != player->cnum)
                    mpr(own,
                        "%s nuclear device turned %s into a radioactive wasteland\n",
                        cname(player->cnum), xyas(ns.x, ns.y, own));
            }
            changed |= map_set(player->cnum, sect.sct_x, sect.sct_y,
                               dchr[sect.sct_type].d_mnem, 0);
        } else {
            pr("did %d%% damage in %s\n",
               damage, xyas(ns.x, ns.y, player->cnum));
            if (own != player->cnum)
                mpr(own, "%s nuclear device did %d%% damage in %s\n",
                    cname(player->cnum), damage, xyas(ns.x, ns.y, own));
        }
        (void)putsect(&sect);
        if (type != SCT_WATER)
            nreport(player->cnum, N_NUKE, own, 1);
    }

    if (changed)
        writebmap(player->cnum);

    snxtitem_dist(&ni, EF_PLANE, x, y, rad);
    while (nxtitem(&ni, &plane)) {
        if ((own = plane.pln_own) == 0)
            continue;
        if (plane.pln_flags & PLN_LAUNCHED)
            continue;
        damage = nukedamage(ncp, ni.curdist, airburst) - plane.pln_harden;
        if (damage <= 0)
            continue;
        if (plane.pln_ship >= 0) {
            /* Are we on a sub? */
            getship(plane.pln_ship, &ship);

            if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
                struct sctstr sect1;

                /* Should we damage this sub? */
                getsect(ship.shp_x, ship.shp_y, &sect1);

                if (sect1.sct_type == SCT_BSPAN ||
                        sect1.sct_type == SCT_BTOWER ||
                        sect1.sct_type == SCT_WATER) {
                    /* Ok, we're not in a harbor or trapped
                       inland.  Now, did we get pasted
                       directly? */
                    if (ship.shp_x != x || ship.shp_y != y) {
                        /* Nope, so don't mess with it */
                        continue;
                    }
                }
            }
        }
        planedamage(&plane, damage);
        if (own == player->cnum) {
            pr("%s at %s reports %d%% damage\n",
               prplane(&plane),
               xyas(plane.pln_x, plane.pln_y, player->cnum), damage);
        } else {
            mpr(own, "%s nuclear device did %d%% damage to %s at %s\n",
                cname(player->cnum), damage,
                prplane(&plane), xyas(plane.pln_x, plane.pln_y, own));
        }
        putplane(ni.cur, &plane);
    }

    snxtitem_dist(&ni, EF_LAND, x, y, rad);
    while (nxtitem(&ni, &land)) {
        if ((own = land.lnd_own) == 0)
            continue;
        if ((damage = nukedamage(ncp, ni.curdist, airburst)) <= 0)
            continue;

        if (land.lnd_ship >= 0) {
            /* Are we on a sub? */
            getship(land.lnd_ship, &ship);

            if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
                struct sctstr sect1;

                /* Should we damage this sub? */
                getsect(ship.shp_x, ship.shp_y, &sect1);

                if (sect1.sct_type == SCT_BSPAN ||
                        sect1.sct_type == SCT_BTOWER ||
                        sect1.sct_type == SCT_WATER) {
                    /* Ok, we're not in a harbor or trapped
                       inland.  Now, did we get pasted
                       directly? */
                    if (ship.shp_x != x || ship.shp_y != y) {
                        /* Nope, so don't mess with it */
                        continue;
                    }
                }
            }
        }
        land_damage(&land, damage);
        if (own == player->cnum) {
            pr("%s at %s reports %d%% damage\n",
               prland(&land), xyas(land.lnd_x, land.lnd_y, player->cnum),
               damage);
        } else {
            mpr(own, "%s nuclear device did %d%% damage to %s at %s\n",
                cname(player->cnum), damage,
                prland(&land), xyas(land.lnd_x, land.lnd_y, own));
        }
        putland(land.lnd_uid, &land);
    }

    snxtitem_dist(&ni, EF_SHIP, x, y, rad);
    while (nxtitem(&ni, &ship)) {
        if ((own = ship.shp_own) == 0)
            continue;
        if ((damage = nukedamage(ncp, ni.curdist, airburst)) <= 0)
            continue;
        if (mchr[(int)ship.shp_type].m_flags & M_SUB) {
            struct sctstr sect1;

            /* Should we damage this sub? */
            getsect(ship.shp_x, ship.shp_y, &sect1);

            if (sect1.sct_type == SCT_BSPAN ||
                    sect1.sct_type == SCT_BTOWER ||
                    sect1.sct_type == SCT_WATER) {
                /* Ok, we're not in a harbor or trapped
                   inland.  Now, did we get pasted
                   directly? */
                if (ship.shp_x != x || ship.shp_y != y) {
                    /* Nope, so don't mess with it */
                    continue;
                }
            }
        }
        ship_damage(&ship, damage);
        if (own == player->cnum) {
            pr("%s at %s reports %d%% damage\n",
               prship(&ship), xyas(ship.shp_x, ship.shp_y, player->cnum),
               damage);
        } else {
            mpr(own, "%s nuclear device did %d%% damage to %s at %s\n",
                cname(player->cnum), damage, prship(&ship),
                xyas(ship.shp_x, ship.shp_y, own));
        }
        putship(ship.shp_uid, &ship);
    }

    snxtitem_dist(&ni, EF_NUKE, x, y, rad);
    while (nxtitem(&ni, &nuke)) {
        if ((own = nuke.nuk_own) == 0)
            continue;
        if ((damage = nukedamage(ncp, ni.curdist, airburst)) <= 0)
            continue;
        if (!pct_chance(damage))
            continue;
        nuke.nuk_effic = 0;
        if (own == player->cnum) {
            pr("%s at %s destroyed\n",
               prnuke(&nuke), xyas(nuke.nuk_x, nuke.nuk_y, player->cnum));
        } else {
            mpr(own, "%s nuclear device destroyed %s at %s\n",
                cname(player->cnum), prnuke(&nuke),
                xyas(nuke.nuk_x, nuke.nuk_y, own));
        }
        putnuke(ni.cur, &nuke);
    }

    return nukedamage(ncp, 0, airburst);
}